Вы можете выяснить, что делает компилятор C #, посмотрев на его источник и спецификацию C #.
Если вы посмотрите на код в компиляторе C #, который обрабатывает выражение объединения для деревьев выражений , вы заметите, что он использует conversion
только в том случае, если левое подвыражение содержит пользовательское выражение.
Затем вы можете посмотреть в разделе Нулевой оператор объединения спецификации C # , чтобы увидеть, когда это произойдет:
В частности, a ?? b
обрабатывается следующим образом:
- Если существует
A
ине является обнуляемым типом или ссылочным типом, возникает ошибка времени компиляции. - […]
- В противном случае, если
b
имеет тип B
и существует неявное преобразование изa
до B
, тип результата - B
.Во время выполнения сначала оценивается a
.Если a
не равно нулю, a
разворачивается в тип A0
(если A
существует и допускает обнуление) и преобразуется в тип B
, и это становится результатом.В противном случае b
вычисляется и становится результатом. - […]
Поэтому нам нужен тип A
, который имеет неявное пользовательское преобразование в B
и используйте эти два типа в объединяющем ноль выражении:
class A
{
public static implicit operator B(A s) => null;
}
class B {}
…
Expression<Func<B>> e = () => new A() ?? new B();
Если вы декомпилируете этот код, вы увидите :
NewExpression left = Expression.New(typeof(A));
NewExpression right = Expression.New(typeof(B));
ParameterExpression parameterExpression = Expression.Parameter(typeof(A), "p");
UnaryExpression body = Expression.Convert(
parameterExpression, typeof(B),
(MethodInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/));
ParameterExpression[] obj = new ParameterExpression[1];
obj[0] = parameterExpression;
Expression.Lambda<Func<B>>(
Expression.Coalesce(left, right, Expression.Lambda(body, obj)), Array.Empty<ParameterExpression>());
Заменить GetMethodFromHandle
с кодом отражения для получения A.op_Implicit
и у вас есть код для создания действительного Coalesce
дерева выражений с ненулевым Conversion
.