Для чего нужен параметр преобразования Expression.Coalesce? - PullRequest
0 голосов
/ 03 декабря 2018

Контекст этого вопроса см. В документации по перегрузке Coalesce (Expression, Expression, LambdaExpression) метода Expression.Coalesce .Я имею в виду конкретно третий параметр этой перегрузки.У меня есть несколько вопросов по этому поводу, на которые я нигде не смог найти ответы, включая документацию Microsoft:

  • Почему кто-то решил использовать эту перегрузку для обеспечения конверсии?
  • Как это будет использоваться при компиляции выражения?
  • Как должен быть построен один проход LambdaExpression (я могу только предположить, что определенная сигнатура параметра и тип возвращаемого значения ожидаются)?

Я неоднократно пытался (приводя различные лямбда-функции, использующие оператор ?? в коде к Expression<>), заставить компилятор C # составить для меня дерево выражений, которое использует этот параметр.Но каждый раз, когда я использую отладчик для проверки свойства следствия параметра преобразования для выражения с NodeType Coalesce в результирующем дереве, оно равно null.

Iя спрашиваю, потому что я работаю над библиотекой, которая работает путем анализа деревьев выражений, и мне нужно, чтобы она правильно понимала и поддерживала эти преобразования.

1 Ответ

0 голосов
/ 03 декабря 2018

Вы можете выяснить, что делает компилятор C #, посмотрев на его источник и спецификацию C #.

Если вы посмотрите на код в компиляторе C #, который обрабатывает выражение объединения для деревьев выражений , вы заметите, что он использует conversion только в том случае, если левое подвыражение содержит пользовательское выражение.

Затем вы можете посмотреть в разделе Нулевой оператор объединения спецификации C # , чтобы увидеть, когда это произойдет:

В частности, a ?? b обрабатывается следующим образом:

  • Если существует A ине является обнуляемым типом или ссылочным типом, возникает ошибка времени компиляции.
  • […]
  • В противном случае, если b имеет тип B и существует неявное преобразование изa до B, тип результата - B.Во время выполнения сначала оценивается a.Если a не равно нулю, a разворачивается в тип A0 (если A существует и допускает обнуление) и преобразуется в тип B, и это становится результатом.В противном случае b вычисляется и становится результатом.
  • […]

Поэтому нам нужен тип A, который имеет неявное пользовательское преобразование в B и используйте эти два типа в объединяющем ноль выражении:

class A
{
    public static implicit operator B(A s) => null;
}

class B {}

…

Expression<Func<B>> e = () => new A() ?? new B();

Если вы декомпилируете этот код, вы увидите :

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

Заменить GetMethodFromHandle с кодом отражения для получения A.op_Implicit и у вас есть код для создания действительного Coalesce дерева выражений с ненулевым Conversion.

...