Search code examples
c#expression-treesimplicit-conversion

Make use of implicit conversion operator inside expression tree


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?


Solution

  • 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.