7

Suppose we have two classes:

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

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

The task is to express the following code in linq expressions:

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

To do that I tried to use the following code:

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

Running this code with

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

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

throws an ArgumentException at the last line (at the return statement in the GetCondition), saying that the types of arguments are mismatched.

Having analysed this code I found out that Activator.CreateInstance<TChildKey>() returns object, but not TChildKey, when TChildKey is System.Nullable<T>, i.e. ifTrue.Type is object, while I expected it to be System.Nullable<byte>.

This very issue is discussed in SO: Creating a nullable object via Activator.CreateInstance returns null pointing to Reflection and Nullable

But none of these suggests anything to solve the issue.

Is there a way to create an instance of exactly System.Nullable<T> type, having value null? Or maybe there is some other way to express the original conditional expression?

Community
  • 1
  • 1
horgh
  • 17,918
  • 22
  • 68
  • 123

3 Answers3

2

You should try Expression.New:

var ifTrue = Expression.New(typeof(Nullable<int>));
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
0

Will this work for you

    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);
    }
lordkain
  • 3,061
  • 1
  • 13
  • 18
  • This will not work either as `ifTrue.Type` will still be `Object` for `default(TChildKey)`. – horgh Feb 25 '14 at 08:15
0

Another option is to explicitly specify what type of constant is being created by Expression.Constant using the two parameter function overload.

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

Nullable<> requires some very careful handling, particularly around nulls. Being explicit is helpful and often necessary when doing anything outside the norm.

Kevin Fee
  • 495
  • 4
  • 17