Почему необязательные параметры C # 4, определенные в интерфейсе, не применяются в классе реализации?

361

Я заметил, что с необязательными параметрами в C # 4, если вы указываете необязательный параметр на интерфейсе, который вам не нужен, вы должны сделать этот параметр необязательным для любого реализующего класса:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

и поэтому:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

Кто-нибудь знает, почему дополнительные параметры предназначены для работы таким образом?

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

С другой стороны, это отключение означает, что вы не всегда можете использовать конкретный класс и интерфейс взаимозаменяемо. Это, конечно, не будет проблемой, если в реализации указано значение по умолчанию, но если вы представляете конкретный класс в качестве интерфейса (например, с помощью некоторой инфраструктуры IOC для внедрения конкретного класса), то на самом деле нет точка, имеющая значение по умолчанию, так как вызывающая сторона должна всегда предоставлять его в любом случае.

theburningmonk
источник
22
Потому что они необязательны?
Одед
1
Но вы можете бросить экземпляр объекта к MyInterfaceи вызвать его с дополнительным параметром: ((MyInterface)obj).TestMethod();.
Джим Мишель
7
@oded - но если вы говорите, что этот параметр является необязательным в контракте, почему вы позволяете исполнителю не делать его необязательным? разве это не вызывает путаницу у тех, кто хочет использовать контракт?
theburningmonk
1
Я думаю, что в этом случае вы можете сказать, что параметр является необязательным при реализации, а не при вызове методов реализации. Когда вы вызываете метод в классе, вы должны следовать правилам класса (параметр не является необязательным в классе, поэтому вы можете не вызывайте метод без него), и во-вторых, когда вы реализуете интерфейс, вы должны следовать правилам интерфейса, чтобы вы могли переопределить методы с / без дополнительных параметров. Просто мнение.
Мохамад Алхамуд
2
Более подробное объяснение проблемы здесь -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Эндрю Оршич

Ответы:

236

ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога 12 мая 2011 года. Спасибо за отличный вопрос!

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

Предположим, мы сделали это. Теперь предположим, что у разработчика не было исходного кода для реализации:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

Как автор D должен сделать эту работу? Обязаны ли они в вашем мире позвонить автору B по телефону и попросить его отправить им новую версию B, в которой метод имеет необязательный параметр?

Это не будет летать. Что делать, если два человека позвонят автору B, и один из них хочет, чтобы значение по умолчанию было истинным, а один из них хотел бы, чтобы оно было ложным? Что если автор Б просто отказывается подыгрывать?

Возможно, в этом случае они должны будут сказать:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

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


ОБНОВЛЕНИЕ: В комментариях ниже, суперкат предлагает языковую особенность, которая действительно добавила бы мощности в язык и включила бы некоторые сценарии, подобные описанному в этом вопросе. К вашему сведению, эта функция - реализация методов по умолчанию в интерфейсах - будет добавлена ​​в C # 8.

Эрик Липперт
источник
9
@supercat: У нас нет никаких планов, но такая возможность возможна. Это было предложено ранее. В процессе проектирования для C # 4 мы разбирались со многими функциями, которые мы называли «все расширение» - у нас есть методы расширения, так что бы это значило иметь события расширения, свойства расширения, конструкторы расширения, интерфейсы расширения и так далее , Классы, которые можно «прикрепить» к интерфейсам и сказать «эти методы являются реализациями этого интерфейса по умолчанию», являются одним из возможных способов охарактеризовать «интерфейсы расширения». Интересная идея
Эрик Липперт
16
чтобы прояснить мои соображения о том, почему я считаю, что это не интуитивно понятно: для меня необязательный параметр является «необязательным для вызывающей стороны метода« НЕ », необязательным для реализатора интерфейса».
Стефан де Кок
8
"Требуются ли они в вашем мире, чтобы позвонить автору B по телефону и попросить их отправить им новую версию [...]?" Нет, никаких телефонных звонков не требуется. B больше просто не сможет считаться внедрением MyInterface, и автор D может сообщить об этом компилятору. Автору D потребуется реализовать D с помощью TestMethod, который принимает параметр по умолчанию, поскольку именно этого требует автор интерфейса - вы выступаете против того, чтобы разрешить автору интерфейса применять ограничение, потому что кто-то может захотеть его нарушить. Это не хороший аргумент.
philofinfinitejest
7
@EricLippert В случае, когда для удобства кодирования указывается необязательный параметр, да, принудительное использование неудобств в реализации противоречит интуиции. Но в случае, когда необязательный параметр используется для минимизации перегрузки метода, что является мощной функцией, эти необязательные значения могут иметь большое значение, и их игнорирование, безусловно, снижает эту мощность. Не говоря уже о случае, когда конкретная реализация задает значение по умолчанию, отличное от интерфейса. Это просто странное отключение, чтобы позволить.
philofinfinitejest
8
Я согласен с @philofinfinitejest здесь. Интерфейс сообщает, что можно сделать. Если мне передают реализацию интерфейса со значением по умолчанию, отличным от указанного в интерфейсе, как мне это узнать? Эй, у меня есть интерфейс, который по умолчанию принимает значение true, так почему эта штука становится ложной? Кажется, что я НЕ получил интерфейс, который ожидал. Хуже того, теперь я должен программировать на реализацию, а не интерфейс.
Ааронбурро
47

Необязательный параметр просто помечается атрибутом. Этот атрибут указывает компилятору вставить значение по умолчанию для этого параметра на сайте вызова.

Вызов obj2.TestMethod();заменяется на то, obj2.TestMethod(false);когда код C # компилируется в IL, а не во время JIT.

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

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

Вы уже не можете сделать это, если метод интерфейса был реализован явно .

CodesInChaos
источник
1
Можете ли вы заставить разработчиков реализовать явно?
раздавить
30

Поскольку параметры по умолчанию разрешаются во время компиляции, а не во время выполнения. Таким образом, значения по умолчанию относятся не к вызываемому объекту, а к ссылочному типу, через который он вызывается.

Ольховский
источник
7

Необязательные параметры напоминают подстановку макросов из того, что я понимаю. Они не являются действительно необязательными с точки зрения метода. Артефактом этого является поведение, которое вы видите, когда вы получаете разные результаты, если переходите к интерфейсу.

Ариэль Аржона
источник