#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, поэтому на момент написания вашего поста это было невозможно.