Как создать экземпляр Nullable?

Предположим, у нас есть два класса:

public class ParentEntity
{
    public ChildEntity Child { get; set; }
}

public class ChildEntity
{
    public byte? NullableValue { get; set; }
    public byte Value { get; set; }
}

задача состоит в том, чтобы выразить следующий код в выражениях linq:

parent.Child == null ? null : parent.Child.NullableValue

для этого я попытался использовать следующий код:

public static Expression GetCondition<TParent, TChild, TChildKey>(
                              Expression<Func<TParent, TChild>> pe, 
                              Expression<Func<TChild, TChildKey>> ce)
{
    var test = Expression.Equal(pe.Body, Expression.Constant(null));

    var ifTrue = Expression.Constant(Activator.CreateInstance<TChildKey>());
    var ifFalse = Expression.Property(pe.Body, 
                                      (ce.Body as MemberExpression).Member.Name);
    return Expression.Condition(test, ifTrue, ifFalse);
}

запуск этого кода с помощью

Expression<Func<ParentEntity, ChildEntity>> pe = n => n.Child;    

GetCondition(pe, n => n.Value);         // ok
GetCondition(pe, n => n.NullableValue); // throws an ArgumentException

выдает ArgumentException в последней строке (в инструкции return в GetCondition), заявив, что типы аргументов не совпадают.

проанализировав этот код, я обнаружил, что Activator.CreateInstance<TChildKey>() возвращает object, но не TChildKey, когда TChildKey и System.Nullable<T>, то есть ifTrue.Type is object, в то время как я ожидал, что это будет System.Nullable<byte>.

этот вопрос обсуждается в SO: создание объекта nullable с помощью активатора.CreateInstance возвращает null указывая на отражение и Nullable

но ни один из них не предлагает ничего, чтобы решить эту проблему.

есть ли способ создать экземпляр точно System.Nullable<T> типа, имеющие значение null? Или, может быть, есть какой-то другой способ выразить исходное условное выражение?

3 ответов


вы должны попробовать Expression.New:

var ifTrue = Expression.New(typeof(Nullable<int>));

эта работа для вас!

    public static Expression GetCondition<TParent, TChild, TChildKey>(
                          Expression<Func<TParent, TChild>> pe,
                          Expression<Func<TChild, TChildKey>> ce)
    {
        var test = Expression.Equal(pe.Body, Expression.Constant(null));

        ConstantExpression ifTrue;
        Type type = typeof(TChildKey);
        // check if it is a nullable type
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>))
        {
            ifTrue = Expression.Constant(default(TChildKey));
        }
        else
        {
            ifTrue = Expression.Constant(   Activator.CreateInstance<TChildKey>());
        }

        var ifFalse = Expression.Property(pe.Body, (ce.Body as MemberExpression).Member.Name);

        return Expression.Condition(test, ifFalse, ifFalse);
    }

другой вариант-явно указать, какой тип константы создается Expression.Constant используя перегрузку функции двух параметров.

var ifTrue = Expression.Constant(Activator.CreateInstance<TChildKey>(), typeof(TChildKey));

Nullable<> требует очень тщательной обработки, особенно вокруг нулей. Быть явным полезно и часто необходимо, когда вы делаете что-либо вне нормы.