Рассмотрим следующие классы:
public class A {
public B GetB() {
Console.WriteLine("GetB");
return new B();
}
}
public class B {
[System.Diagnostics.Conditional("DEBUG")]
public void Hello() {
Console.WriteLine("Hello");
}
}
Теперь, если мы будем называть методы таким образом:
var a = new A();
var b = a.GetB();
b.Hello();
В релиз-сборке (т. DEBUG
флаг DEBUG
), мы увидели бы только GetB
напечатанный на консоли, так как вызов Hello()
будет отсутствовать компилятором. В отладочной сборке появятся оба отпечатка.
Теперь позвольте цепочке вызов метода:
a.GetB().Hello();
Поведение в сборке отладки не изменилось; однако мы получаем другой результат, если флаг не установлен: оба вызова опускаются и на консоли не отображаются отпечатки. Быстрый взгляд на IL показывает, что вся строка не была скомпилирована.
Согласно последнему стандарту ECMA для С# (ECMA-334, то есть С# 5.0), ожидаемое поведение, когда атрибут Conditional
помещается в этот метод, выглядит следующим образом (основное внимание):
Вызов условного метода включается, если один или несколько ассоциированных условных символов компиляции определены в точке вызова, иначе вызов будет опущен. (§22.5.3)
Это, по-видимому, не указывает на то, что всю цепочку следует игнорировать, поэтому мой вопрос. Тем не менее, спецификация С# 6.0 от Microsoft предлагает немного более подробную информацию:
Если символ определен, вызов включается; в противном случае вызов (включая оценку получателя и параметры вызова) опускается.
Тот факт, что параметры вызова не оцениваются, хорошо документирован, поскольку одна из причин, по которым люди используют эту функцию, а не директивы #if
в теле функции. Тем не менее, часть о "оценке приемника" является новой - я не могу найти ее в другом месте, и, похоже, это объясняет описанное выше поведение.
В свете этого, мой вопрос заключается в следующем: какое обоснование компилятора С# не оценивает a.GetB()
в этой ситуации? Должно ли оно вести себя по-разному на основе того, хранится ли получатель условного вызова во временной переменной или нет?