Используйте оператор неявного преобразования внутри дерева выражений - PullRequest
0 голосов
/ 05 ноября 2019

Предположим, у меня есть следующий тип с оператором неявного преобразования:

public readonly struct WrappedInt
{
    public WrappedInt(int value)
    {
        Value = value;
    }

    public int Value { get; }

    public static implicit operator int (WrappedInt wrapper) => wrapper.Value;
}

Тогда вот приложение:

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);
}

Обратите внимание, что в функции Unwrap я делаюint result = wrapper;, который использует мой оператор неявного преобразования - отлично.

Теперь я хочу сделать то же самое, но в дереве выражений - UnwrapWithExpression рутина. Здесь я назначаю свою переменную «result» напрямую параметру «wrapper», используя

Expression right = wrapperParameterExpression;

Но это не работает - я получаю исключение времени выполнения:

System.ArgumentException: 'Выражение типа' WrappedInt 'не может использоваться для присвоения типу' System.Int32 '.

Я знаю, как обойти это, в основном любой доступ к Value свойство:

Expression right = Expression.Property(wrapperParameterExpression, nameof(WrappedInt.Value));

Или преобразовать его:

Expression right = Expression.Convert(wrapperParameterExpression, typeof(int));

Но почему я не могу просто использовать исходное прямое назначение и сделать мойнеявный оператор делает работу?

1 Ответ

1 голос
/ 05 ноября 2019

Выражения во многих отношениях более строгие, чем в C #, особенно в отношении неявных преобразований типов. Вы найдете много случаев, когда компилятор C # вставит преобразование типа для вас, но вам нужно добавить явное Expression.Convert.

Если вы хотите вызвать метод неявного преобразования, вам нужно будет добавитьExpression.Convert. Так работают выражения, к лучшему или к худшему. Это не обходной путь.

Если вы напишите выражение:

Expression<Func<WrappedInt, int>> convert = x => x;

, а затем посмотрите на convert.DebugView в отладчике, вы увидите следующее:

.Lambda #Lambda1<System.Func`2[WrappedInt,System.Int32]>(WrappedInt $x) {
    (System.Int32)$x
}

То, что (System.Int32)$x - это узел Convert (это можно проверить, посмотрев на convert.Body в отладчике). Компилятор решил, что лучшим эквивалентом этого C # в стране выражений является Expression.Convert, поэтому вам тоже следует это сделать.

...