Это ошибка ExpressionTrees? # 2

Похоже, что компилятор ExpressionTrees должен быть рядом со спецификацией С# во многих вариантах поведения, но в отличие от С# нет поддержки для преобразования из decimal в любой enum-type:

using System;
using System.Linq.Expressions;

class Program
{
  static void Main()
  {
    Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor) x;
    ConsoleColor c1 = converter1(7m); // fine

    Expression<Func<decimal, ConsoleColor>> expr = x => (ConsoleColor) x;

    // System.InvalidOperationException was unhandled
    // No coercion operator is defined between types
    // 'System.Decimal' and 'System.ConsoleColor'.

    Func<decimal, ConsoleColor> converter2 = expr.Compile();

    ConsoleColor c2 = converter2(7m);
  }
}

Другие редко используемые явные преобразования С#, такие как double -> enum-type, существуют и работают, как описано в спецификации С#, но не decimal -> enum-type. Это ошибка?

Ответ 1

Вероятно, это ошибка, и, вероятно, это моя ошибка. Извините.

Правильное преобразование десятичных преобразований было одной из самых сложных частей построения правильного кода дерева выражений в компиляторе и среде выполнения, поскольку десятичные преобразования фактически реализованы как пользовательские преобразования во время выполнения, но рассматриваются как встроенные преобразования с помощью компилятор. Десятичный - это единственный тип с этим свойством, и поэтому в этих анализах есть все виды специального назначения в анализаторе. Фактически, в анализаторе есть метод, называемый IsEnumToDecimalConversion, для обработки специального случая нумерованного перечисления с нулевым десятичным преобразованием; довольно сложный частный случай.

Коэффициенты хороши, что я не смог рассмотреть случай, когда происходит другой путь, и в результате генерировал плохой код. Спасибо за примечание; Я отправлю это в тестовую группу, и мы посмотрим, сможем ли мы получить репродукцию. Коэффициенты хороши тем, что если это окажется ошибкой bona fide, это не будет исправлено для начального выпуска С# 4; на данный момент мы принимаем только ошибки "пользователь отключен от компилятора", чтобы релиз был стабильным.

Ответ 2

Не настоящий ответ, я изучаю, но первая строка скомпилирована как:

Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor)(int)x;

Если вы попытаетесь создать выражение из предыдущей лямбда, оно будет работать.

EDIT: в спецификации С#, §6.2.2, вы можете прочитать:

Явное преобразование перечислений между двумя типами обрабатывается лечение любого участвующего типа перечисления как основной тип этого enum-type, а затем выполнить неявный или явный числовой преобразование между полученным типы. Например, с учетом типа перечисления E с и базовым типом int, a выполняется преобразование из E в байт как явное числовое преобразование (§6.2.1) от int до байта, и выполняется преобразование из байта в E как неявное числовое преобразование (§6.1.2) от байта до int.

Так явным образом отбрасываются от перечисления до десятичного числа, поэтому вы получаете вложенные броски (int then decimal). Но я не понимаю, почему компилятор не анализирует тело лямбда одинаково в обоих случаях.