For context on this question, please see the documentation for the Coalesce(Expression, Expression, LambdaExpression) overload of the Expression.Coalesce Method. I am referring specifically to the third parameter of this overload. I have a few questions about it for which I have been unable to find answers anywhere, including Microsoft's documentation:
LambdaExpression
one passes be constructed (I can only assume a specific parameter signature and return value type will be expected)?I have repeatedly tried (by casting various lambda functions that employ the ??
operator in code to Expression<>
) to get the C# compiler to compose an expression tree for me which makes use of this parameter. But every time I use the debugger to check the corollary property of the conversion parameter for the expression with NodeType
Coalesce
in the resulting tree, it is null
.
I'm asking because I'm working on a library that works by analyzing expression trees and I need it to properly understand and support these conversions.
You can figure out what the C# compiler does by looking at its source and the C# specification.
If you look at the code in the C# compiler that handles the coalesce expression for expression trees, you'll notice that it only uses conversion
when the left sub-expression contains a user-defined expression.
You can then look at the section The null coalescing operator of the C# spec to see when that happens:
Specifically,
a ?? b
is processed as follows:
- If
A
exists and is not a nullable type or a reference type, a compile-time error occurs.- […]
- Otherwise, if
b
has a typeB
and an implicit conversion exists froma
toB
, the result type isB
. At run-time,a
is first evaluated. Ifa
is not null,a
is unwrapped to typeA0
(ifA
exists and is nullable) and converted to typeB
, and this becomes the result. Otherwise,b
is evaluated and becomes the result.- […]
So we need type A
that has implicit user-defined conversion to B
and use those two types in a null-coalescing expression:
class A
{
public static implicit operator B(A s) => null;
}
class B {}
…
Expression<Func<B>> e = () => new A() ?? new B();
If you decompile this code, you'll see:
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>());
Replace GetMethodFromHandle
with reflection code to get A.op_Implicit
and you have code to create a valid Coalesce
expression tree with non-null Conversion
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With