Необязательные параметры и наследование

#c# #inheritance #optional-parameters

#c# #наследование #необязательно-параметры

Вопрос:

Я понимаю необязательные параметры, и они мне вполне нравятся, но я просто хотел бы узнать немного больше об их использовании с унаследованным интерфейсом.

Демонстрируют

 interface IMyInterface
{
    string Get();
    string Get(string str);
}

class MyClass : IMyInterface
{
    public string Get(string str = null)
    {
        return str;
    }
}
  

Теперь я бы подумал, что Get метод в MyClass наследует оба метода интерфейса, но…

‘MyClass’ не реализует элемент интерфейса ‘MyInterface.Get()’

Есть ли веская причина для этого?

Возможно, мне следует сделать это, поместив необязательные параметры в интерфейс, который вы говорите? Но как насчет этого?

Пример B

 interface IMyInterface
{
    string Get(string str= "Default");
}

class MyClass : IMyInterface
{
    public string Get(string str = "A different string!")
    {
        return str;
    }
}
  

И этот код отлично компилируется. Но это, конечно, не может быть правильным? Затем, немного покопавшись, я обнаружил это:

   IMyInterface obj = new MyClass();
  Console.WriteLine(obj.Get()); // writes "Default"

  MyClass cls = new MyClass();
  Console.WriteLine(cls.Get()); // writes "A different string!"
  

Казалось бы, вызывающий код получает значение необязательного параметра на основе объявленного типа объектов, а затем передает его методу. Мне это кажется немного глупым. Может быть, у необязательных параметров и перегрузок метода есть свои сценарии, когда их следует использовать?

Мой вопрос

Моему вызывающему коду передается экземпляр IMyInterface , и ему необходимо вызвать оба метода здесь в разных точках.

Буду ли я вынужден реализовывать одну и ту же перегрузку метода в каждой реализации?

 public string Get()
{
  return Get("Default");
}
  

Комментарии:

1. Смотрите Примеры угловых необязательных аргументов Эрика Липперта, часть вторая

2. 1 Это говорит мне о том, что никогда не следует определять интерфейсы с необязательными параметрами. Слишком много двусмысленности.

3. @GertArnold После вопроса я узнал немного больше. Я думаю, что опубликую это как ответ на вопрос, потому что я действительно нашел решение.

4. Продолжайте, здесь это происходит постоянно. (вы знаете, конечно)

5. Я действительно не думаю, что параметры по умолчанию должны быть разрешены в интерфейсах. Они являются деталями реализации.

Ответ №1:

Чего я также не понял, так это того, что необязательные параметры не изменяют сигнатуру метода. Итак, следующий код совершенно легален и на самом деле был моим решением:

 interface IMyInterface
{
    string Get(string str = "Default");
}

class MyClass : IMyInterface
{
    public string Get(string str)
    {
        return str;
    }
}
  

Итак, если у меня есть экземпляр MyClass , я должен вызвать Get(string str) , но если этот экземпляр был объявлен как базовый интерфейс IMyInterface , я все равно могу вызвать Get() , который IMyInterface сначала получает значение по умолчанию, а затем вызывает метод.

Комментарии:

1. Подумайте, что вы также могли бы иметь public string Get(string whateverIWant) . Некоторые части интерфейса являются обещаниями (он обещает принимать строку и возвращать string), некоторые — подсказками (он предлагает разрешить вызывающему коду не указывать параметр и заставить компилятор изменить его на вызов с «Default»), а некоторые — документацией (имя параметра не имеет значения, если оно не вызывается по имени).

Ответ №2:

Ваше решение мне не подошло, поскольку я хотел, чтобы каждый дочерний элемент использовал свои собственные значения по умолчанию, используя отражение, я нашел способ заставить это работать :

 interface IMyInterface {
    string MyMethod() {
        BindingFlags bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding;
        this.GetType().InvokeMember("MyMethod",bf,null,this,new object[] {v,i,j,Type.Missing});
    }
    string MyMethod(string param1) { }
}

public class MyClass : IMyInterface
{
    public string MyMethod(string str = "DefaultFromClass") { 
        return str;
     }
}
  

Способ, которым это работает, когда вызывается метод, если он не реализован в классе, он вызывает метод с еще одним необязательным параметром с Type.Missing, который проверяет, реализует ли класс эту перегрузку метода.
Это можно расширить, чтобы иметь более одного параметра, просто продолжая вызывать следующий каждый раз.

Обратите внимание, что мне нужно определить реализацию методов в интерфейсе по умолчанию, которая является функцией, представленной в c # 8.0, поэтому на момент написания вашего поста это было невозможно.