Примітка. Схоже, це було виправлено у Росліні
Це запитання виникло під час написання моєї відповіді на цю , в якій йдеться про асоціативність оператора, що поєднує нуль .
Як нагадування, ідея оператора узгодження нуля - це вираз форми
x ?? y
спочатку оцінює x
, потім:
- Якщо значення
x
null,y
оцінюється, і це кінцевий результат виразу - Якщо значення
x
не дорівнює нуль,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.
Я можу зрозуміти і другий випадок, і третій випадок ... але чому в першому випадку є додаткове перетворення від A до 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()
дзвонять сюди двічі, мене надзвичайно дивує - я не бачу жодної причини, щоб вираз оцінювався двічі.
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Ви отримаєте:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")