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 ?? bis processed as follows:
- If
Aexists and is not a nullable type or a reference type, a compile-time error occurs.- […]
- Otherwise, if
bhas a typeBand an implicit conversion exists fromatoB, the result type isB. At run-time,ais first evaluated. Ifais not null,ais unwrapped to typeA0(ifAexists and is nullable) and converted to typeB, and this becomes the result. Otherwise,bis 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