Сложности с необязательными параметрами. Часть 3
(Это вторая часть из серии сообщений о сложностях с необязательными параметрами в языке C# 4; вторая часть находится здесь).
Многие люди считают, что следующий код:
void M(string x, bool y = false) { ... некоторый код ... }
На самом деле является синтаксическим сахаром для следующего привычного кода на языке C#:
void M(string x) { M(x, false); }
void M(string x, bool y) { ... некоторый код ... }
Но это не так. Синтаксический сахар находится не в месте объявления метода, а в месте его вызова. Существует только один метод, и когда вы его вызываете, не указывая необязательные параметры, компилятор просто вставляет эти параметры в месте вызова. Т.е.
M("hello");
заменяется на:
M("hello", false);
Было бы весьма странно, если бы мы выполняли замену в месте объявления, тогда что бы нам пришлось делать в этом случае?
void N(bool a1 = false, bool a2 = false) { ... некоторый код ... }
Очевидно, мы не можем генерировать следующие варианты:
void N() { N(false, false); }
void N(bool a1) { N(a1, false); }
void N(bool a2) { N(false, a2); }
void N(bool a1, bool a2) { ... некоторый код ... }
Поскольку в этом случае мы получаем два метода с одинаковой сигнатурой. Но зачем вообще нам нужно генерировать метод, принимающий параметр «a2»? Потому что помимо необязательных параметров, мы еще добавили именованные параметры. Кто-то может вызвать метод N следующим образом:
N(a2: true);
Именно поэтому, нам нужно вносить изменения в месте вызова, а не в вызываемом методе.
Параметры по умолчанию вообще не изменяют сигнатуру метода, так что любой код, зависящий от совпадения сигнатур, по-прежнему требует точного совпадения. Поэтому, даже если вы можете написать такой код:
M("hello");
И такой:
Action<string> action = (string s)=>{M(s);};
Вы не можете написать такой:
Action<string> action = M;
Поскольку сигнатура метода M не совпадает с сигнатурой типа делегата; делегат ожидает метод, принимающий строку, но вы передаете метод, принимающий строку и bool. Где должен располагаться код, устанавливающий нужное значение булевого параметра? В этом коде нет места вызова (call site), а значение по умолчанию устанавливается именно в месте вызова.
Аналогично, вы не можете написать следующий код:
class B
{
public virtual void M(string x, bool y = false) {}
}
class D : B
{
public override void M(string x) {}
}
Или такой:
class D : B
{
public override void M(string x, bool y = false, int z = 123) {}
}
При переопределении метода сигнатуры должны совпадать, а значения по умолчанию не являются частью сигнатуры.
В следующий раз: дополнительные последствия изменения мест вызова.
(Это вторая часть из серии материалов о сложностях с необязательными параметрами в языке C# 4; вторая часть находится здесь).
Comments
- Anonymous
May 20, 2011
На самом деле это третья часть из серии статей посвященных сложностям с необязательными параметрами в C# 4. Поправьте, пожалуйста, перевод в начале и в конце статьи.