Примечание: это, по-видимому, исправлено в Roslyn
Этот вопрос возник при написании моего ответа на этот, в котором говорится об ассоциативности нуль-коалесцирующий оператор.
Как напоминание, идея оператора нуль-коалесцирования состоит в том, что выражение вида
x ?? y
сначала оценивает x
, затем:
- Если значение
x
равно null, оцениваетсяy
, и это конечный результат выражения - Если значение
x
не равно null,y
не оценивается, а значениеx
является конечным результатом выражения после преобразования в тип времени компиляцииy
при необходимости
Теперь, как правило, нет необходимости в преобразовании, или просто от нулевого типа до непустого значения - обычно типы одинаковы или только от (скажем) от int?
до int
. Однако вы можете создавать свои собственные неявные операторы преобразования, и они используются там, где это необходимо.
Для простого случая x ?? y
я не видел какого-либо нечетного поведения. Тем не менее, с (x ?? y) ?? z
я вижу некоторое запутанное поведение.
Здесь короткая, но полная тестовая программа - результаты находятся в комментариях:
using System;
public struct A
{
public static implicit operator B(A input)
{
Console.WriteLine("A to B");
return new B();
}
public static implicit operator C(A input)
{
Console.WriteLine("A to C");
return new C();
}
}
public struct B
{
public static implicit operator C(B input)
{
Console.WriteLine("B to C");
return new C();
}
}
public struct C {}
class Test
{
static void Main()
{
A? x = new A();
B? y = new B();
C? z = new C();
C zNotNull = new C();
Console.WriteLine("First case");
// This prints
// A to B
// A to B
// B to C
C? first = (x ?? y) ?? z;
Console.WriteLine("Second case");
// This prints
// A to B
// B to C
var tmp = x ?? y;
C? second = tmp ?? z;
Console.WriteLine("Third case");
// This prints
// A to B
// B to C
C? third = (x ?? y) ?? zNotNull;
}
}
Итак, у нас есть три пользовательских типа значений, A
, B
и C
, с преобразованиями от A до B, от A до C и от B до C.
Я могу понять как второй случай, так и третий случай... но почему в первом случае возникает дополнительное преобразование А-Б? В частности, я действительно ожидал, что первый случай и второй случай будут одинаковыми - это просто извлечение выражения в локальную переменную.
Кто-нибудь задумывается над тем, что происходит? Я очень сомневаюсь заплакать "ошибка", когда дело доходит до компилятора С#, но я в тупике о том, что происходит...
EDIT: Хорошо, вот более неприятный пример того, что происходит, благодаря ответу на конфигуратор, который дает мне еще больше основания думать, что это ошибка. EDIT: Образец даже не нуждается в двух операторах с нулевым коалесцентом...
using System;
public struct A
{
public static implicit operator int(A input)
{
Console.WriteLine("A to int");
return 10;
}
}
class Test
{
static A? Foo()
{
Console.WriteLine("Foo() called");
return new A();
}
static void Main()
{
int? y = 10;
int? result = Foo() ?? y;
}
}
Результат этого:
Foo() called
Foo() called
A to int
Тот факт, что Foo()
вызывается дважды здесь, мне очень удивляет - я не вижу никакой причины, чтобы выражение оценивалось дважды.