tString = myClass.HasValue ? myClass : null;
Здесь у нас есть троичный. Тройной имеет тип, который определяется из двух аргументов. Компилятор просматривает myClass
и null
, видит, что они совместимы (их можно преобразовать в MyClass<string>
), и определяет, что тип троицы равен MyClass<string>
.
Если myClass.HasValue
равно false
, то мы попадаем в ветку null
троицы. Затем мы получаем MyClass<string>
экземпляр, который null
. Затем нам нужно преобразовать это в string
: чтобы сделать это, компилятор вызывает ваш неявный оператор, но передает null
. Это вызывает ваш NullReferenceException
, потому что вы получаете доступ к x.value
, но x
- это null
.
Этого не происходит с if/else
, потому что мы никогда не создаем MyClass<string>
, который равен null
. Вместо этого мы присваиваем null
непосредственно string
.
Этот более простой пример вызывает то же исключение по той же причине:
MyClass<string> myClass = null;
string s = myClass;
Вы также можете увидеть это, указав тип троицы как string
вместо MyClass<string>
, выполнив:
tString = myClass.HasValue ? myClass : (string)null;
или
tString = myClass.HasValue ? (string)myClass : null;
В этом случае исключение не происходит.
ОП отметил, что этого не происходит для явного оператора. Это неправильно: это так.
tString = (string)(myClass.HasValue ? myClass : null);
Выдает то же исключение по той же причине.
Если вместо этого вы делаете:
tString = myClass.HasValue ? (string)myClass : null;
Затем вы попадаете в тот же случай, как я описал ранее, потому что вы никогда не создаете MyClass<string>
, который является нулевым, и поэтому вы никогда не пытаетесь преобразовать этот null
MyClass<string>
в string
.