Чому визначені необов'язкові параметри 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

Хтось знає, чому додаткові параметри розроблені для роботи таким чином?

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

З іншого боку, цей відключення означає, що ви не завжди можете використовувати бетонний клас та інтерфейс взаємозамінно. Це, звичайно, не буде проблемою, якщо в реалізації буде вказано значення за замовчуванням, але тоді, якщо ви виставляєте конкретний клас як інтерфейс (використовуючи якусь рамку МОК для введення конкретного класу, наприклад), тоді насправді немає точка, яка має значення за замовчуванням, як абонент, повинен буде її завжди надавати.


22
Тому що вони необов’язкові?
Одід

1
Але ви можете кинути екземпляр об'єкта до MyInterfaceі викликати його з додатковим параметром: ((MyInterface)obj).TestMethod();.
Джим Мішель

7
@oded - але якщо ви говорите, що цей параметр є необов’язковим у договорі, чому ви дозволяєте виконавцю не робити його необов’язковим? це не просто викликає плутанину у тих, хто хоче скористатися договором?
theburningmonk

1
Я думаю, що в цьому випадку ви можете сказати, що параметр необов’язковий при реалізації, а не в виклику методів реалізації. Коли ви викликаєте метод у класі, ви повинні слідувати правилам класу (параметр не є обов'язковим у класі, щоб ви могли не називатимуть метод без нього), а з другого боку, коли ви реалізуєте інтерфейс, ви повинні дотримуватися правил інтерфейсу, щоб ви могли замінити методи з / без додаткових параметрів. Просто думка.
Mohamad Alhamoud

2
Більш детальне пояснення проблеми тут -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Андрій Оршич

Відповіді:


235

ОНОВЛЕННЯ: Це питання було предметом мого блогу 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, і одна з них хоче, щоб дефолт був правдивим, і одна з них хоче, щоб він був помилковим? Що робити, якщо автор B просто відмовляється грати?

Можливо, у цьому випадку від них вимагатимуть сказати:

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

Запропонована функція, здається, додає багато незручностей для програміста без відповідного збільшення представницької потужності. Яка переконлива перевага цієї функції, яка виправдовує підвищення вартості для користувача?


ОНОВЛЕННЯ: У коментарях нижче, supercat пропонує мовну функцію, яка справді додасть живлення мові та дозволить створити деякі сценарії, аналогічні описаному в цьому питанні. FYI, ця функція - реалізація за замовчуванням методів в інтерфейсах - буде додана до C # 8.


9
@supercat: Ми не плануємо цього робити, але така функція можлива. Це було запропоновано раніше. Під час проектування для C # 4 ми обминули багато того, що ми назвали функціями "розширення все" - у нас є методи розширення, і що б це означало мати події розширення, властивості розширення, конструктори розширень, інтерфейси розширень тощо . Класи, які ви могли "приєднати" до інтерфейсів і сказати, що "ці методи - це реалізація за замовчуванням цього інтерфейсу", - це один із можливих способів характеризувати "інтерфейси розширення". Цікава ідея.
Ерік Ліпперт

16
для уточнення моїх міркувань, чому я вважаю, що це не інтуїтивно зрозуміло: для мене необов'язковим параметром є "необов'язковий для абонента методу" НЕ "необов'язково для реалізатора інтерфейсу".
Штефан де Кок

8
"Чи вимагають, щоб у вашому світі зателефонували авторові B по телефону та попросили, щоб він надіслав їм нову версію [...]?" Ні, телефонні дзвінки не потрібні. B просто не повинен вважатись реалізацією MyInterface, і автор D міг би знати про це компілятором. Автору D потрібно було б реалізувати D з TestMethod, який приймає параметр за замовчуванням, оскільки саме цього вимагає автор інтерфейсу - ви сперечаєтесь з дозволом автору інтерфейсу накладати обмеження, оскільки хтось може захотіти його порушити. Це не гарний аргумент.
philofinfinitejest

7
@EricLippert У випадку, коли необов'язковий параметр задається для зручності кодування, так, виправлення незручностей у реалізації є контрінтуїтивним. Але у випадку, коли необов'язковий параметр використовується для мінімізації перевантаження методу, потужної функції, ці необов'язкові значення можуть набувати великого значення, ігноруючи їх, безумовно, це зменшує потужність. Не кажучи вже про випадок, коли конкретна реалізація вказує інше значення за замовчуванням, ніж інтерфейс. Це просто здається дуже дивним відключенням, щоб дозволити.
філофінфінітест

8
Я згоден з @philofinfinitejest тут. Інтерфейс повідомляє, що щось можна зробити. Якщо мені передається реалізація інтерфейсу з іншим значенням за замовчуванням, ніж те, що вказано в інтерфейсі, як я можу це знати? Гей, у мене є інтерфейс, який передається істиною як за замовчуванням, так чому ж ця річ стає помилковою? Це, здавалося б, я НЕ отримав інтерфейс, який я очікував. Ще гірше, що мені зараз доводиться програмувати на реалізацію, а не на інтерфейс.
aaronburro

47

Необов'язковий параметр просто позначений атрибутом. Цей атрибут повідомляє компілятору вставити значення за замовчуванням для цього параметра на сайті виклику.

Виклик obj2.TestMethod();замінюється, obj2.TestMethod(false);коли код C # збирається в IL, а не в JIT-час.

Отже, певним чином, виклик завжди надає необов'язкові параметри за замовчуванням. Це також має наслідки для бінарної версії: Якщо ви зміните значення за замовчуванням, але не перекомпілюйте код виклику, він продовжить використовувати старе значення за замовчуванням.

З іншого боку, цей відключення означає, що ви не завжди можете використовувати бетонний клас та інтерфейс взаємозамінно.

Ви вже не можете цього зробити, якщо метод інтерфейсу був реалізований явно .


1
Чи можете ви примусити виконавців до явної реалізації?
розчавити

30

Оскільки параметри за замовчуванням вирішуються під час компіляції, а не під час виконання. Отже значення за замовчуванням належать не тому об'єкту, який викликається, а типу посилань, через який він викликається.


7

Необов’язкові параметри є на зразок макрозаміни з того, що я розумію. Насправді вони не є обов'язковими з точки зору методу. Артефактом цього є поведінка, яку ви бачите, де ви отримуєте різні результати, якщо ви переходите на інтерфейс.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.