Почему преобразование необходимо в деревьях выражений - PullRequest
7 голосов
/ 21 февраля 2011

С этот вопрос Я спросил 5 минут назад, ясно, что следующий код выдает исключение, утверждая, что

Необработанное исключение: System.InvalidOperationException: бинарный оператор Equal не определен для типов 'System.Nullable`1 [System.Int32]' и 'System.Int32'.

код

public static void GetResultCollection<T>() {
        AccrualTrackingEntities db = new AccrualTrackingEntities();
        var result = db.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name + "s"));

        int? ItemTypeValue = 1;

        var param = Expression.Parameter(typeof(T));

        var lambda = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.Property(param, "ProcInstId"),
                Expression.Constant(ItemTypeValue)),
            param);

        var list = result.Where(lambda).ToList();
    }

Однако этот код с типом, явно указанным в Expression.Constant, действительно работает

class Program {
    public static void GetResultCollection<T>() {
        AccrualTrackingEntities db = new AccrualTrackingEntities();
        var result = db.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name + "s"));

        int? ItemTypeValue = 1;

        var param = Expression.Parameter(typeof(T));

        var lambda = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.Property(param, "ProcInstId"),
                Expression.Constant(ItemTypeValue, typeof(int?))),
            param);

        var list = result.Where(lambda).ToList();
    }

Вопрос в том, почему не может Expression.Constant не в состоянии неявно преобразовать из int? в ... int?

1 Ответ

15 голосов
/ 21 февраля 2011

Деревья выражений работают на более низком уровне по сравнению с обычным исходным кодом - вы можете думать о них как о работающих на уровне вывода компилятора, а не ввода . Поэтому, хотя в C # существует неявное преобразование из int в int?, это преобразование должно быть представлено в IL всякий раз, когда компилятор использует его для обычного метода ... поэтому оно также должно присутствовать в представлении дерева выражений.

Сказав это, ваш пример несколько неясен, учитывая, что вы пытаетесь использовать int (а именно ItemTypeValue.Value) в качестве значения для константы int?, и мы не знаем, что это за тип из свойства ItemType - либо.

Короткий, но завершенный пример того, что вы ожидаете от работы, действительно поможет.

РЕДАКТИРОВАТЬ: Хорошо, я думаю, что я с тобой сейчас. Проблема в том, что если вы используете

int? foo = 1;
Expression.Constant(foo);

, тогда это вызывает Expression.Constant(object), что ограничивает значение foo. В этот момент Expression.Constant не может сказать, что изначально это был int?, потому что теперь это int в штучной упаковке. Так работает бокс .NET:

int? foo = 1;
object o = foo;
Console.WriteLine(o.GetType()); // Prints System.Int32

Эта перегрузка Expression.Constant определяет общий тип выражения по заданному значению - поэтому он создает выражение int, тогда как вы действительно хотите выражение int?.

Чтобы правильно поддерживать информацию о типе, вы должны использовать перегрузку, которая позволяет вам указать тип:

int? foo = 1;
Expression.Constant(foo, typeof(int?));

Из вашего вопроса все еще не совсем ясно, какой код работает, а какой нет, но, надеюсь, это поможет ...

...