Suppose I have the following type with an implicit convertion operator:
public readonly struct WrappedInt
{
public WrappedInt(int value)
{
Value = value;
}
public int Value { get; }
public static implicit operator int (WrappedInt wrapper) => wrapper.Value;
}
Then here is an application:
var wrapper = new WrappedInt(42);
int x1 = Unwrap(wrapper);
int x2 = UnwrapUsingExpression(wrapper);
private static int Unwrap(WrappedInt wrapper)
{
int result = wrapper;
return result;
}
private static int UnwrapUsingExpression(WrappedInt wrapper)
{
var wrapperParameterExpression = Expression.Parameter(typeof(WrappedInt), "wrapper");
var resultVariableExpression = Expression.Variable(typeof(int), "result");
Expression right = wrapperParameterExpression; // THIS is important
var assignExpression = Expression.Assign(resultVariableExpression, right);
var blockExpression = Expression.Block(
new ParameterExpression[] { resultVariableExpression },
new Expression[] { resultVariableExpression, assignExpression, resultVariableExpression }
);
var unwrapFunc = Expression.Lambda<Func<WrappedInt, int>>(blockExpression, wrapperParameterExpression).Compile();
return unwrapFunc(wrapper);
}
Please note that in the Unwrap
function I do int result = wrapper;
which uses my implicit convertion operator - fine.
Now I want to do the same but in the expression tree - UnwrapWithExpression
routine. Here I'm assigning my 'result' variable directly to 'wrapper' parameter using
Expression right = wrapperParameterExpression;
But this does not work - I get a runtime exception:
System.ArgumentException: 'Expression of type 'WrappedInt' cannot be used for assignment to type 'System.Int32'.
I know how to work around it, basically either access the Value
property:
Expression right = Expression.Property(wrapperParameterExpression, nameof(WrappedInt.Value));
Or converting it:
Expression right = Expression.Convert(wrapperParameterExpression, typeof(int));
But why I cannot simply use the original direct assignment and make my implicit operator do the work?
Expressions are more restrictive than C# in many ways, particularly around implicit type conversions. You will find many cases where the C# compiler will insert a type conversion for you, but where you need to add an explicit Expression.Convert
.
If you want to call the implicit conversion method, you will need to add an Expression.Convert
. This is how expressions work, for better or for worse. Doing so is not a workaround.
If you write the expression:
Expression<Func<WrappedInt, int>> convert = x => x;
and then look at convert.DebugView
in a debugger, you see this:
.Lambda #Lambda1<System.Func`2[WrappedInt,System.Int32]>(WrappedInt $x) {
(System.Int32)$x
}
That (System.Int32)$x
is a Convert
node (which you can verify by looking at convert.Body
in the debugger). The compiler's decided that the best equivalent to that C# in the land of Expressions is Expression.Convert
, so that is what you should do, too.