Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unboxing nullable types - workaround?

Tags:

c#

I'm storing an update operation as thus:

class Update
{
    public Expression MemberExpression { get; set; }
    public Type FieldType { get; set; }
    public object NewValue { get; set; }
}

For example:

var myUpdate = new Update
{
    MemberExpression = (MyType o) => o.LastModified,
    FieldType = typeof(DateTime?),
    NewValue = (DateTime?)DateTime.Now
}

Then I'm trying to apply this update later (simplified):

var lambda = myUpdate.MemberExpression as LambdaExpression;
var memberExpr = lambda.Body as MemberExpression;
var prop = memberExpr.Member as PropertyInfo;
prop.SetValue(item, myUpdate.Value);

However, myUpdate.Value is a DateTime, not a DateTime?. This is because when you cast a nullable to an object, it either becomes null or boxes the value type.

Since (DateTime?)myUpdate.Value would work to get it back to the correct type, I tried to simulate this compile-time construct using Convert.ChangeType. However, it says that casting from DateTime to DateTime? is not possible.

What approach can I use here to get the object back into its original type?

Is there any way to store a nullable types, regular structs and regular objects in a single field, and get the exact thing stored into it back again?

like image 738
Kieren Johnstone Avatar asked May 22 '15 19:05

Kieren Johnstone


2 Answers

If you know the type of your Expression when you create myUpdate, SetInfo() will properly set your data, even if boxing/unboxing.

public class Update
{
    public Expression MemberExpression { get; set; }
    public Type ClassType { get; set; }
    public object NewValue { get; set; }
}

public class MyType
{
    public DateTime? LastModified { get; set; }
}

void Main()
{
    var myUpdate = new Update
    {
        MemberExpression = 
            (Expression<Func<MyType, DateTime?>>)((MyType o) => o.LastModified),
        ClassType = typeof(MyType),
        NewValue = DateTime.Now
    };

    // At a later point where we do not want to instantiate via strong typing
    var item = Activator.CreateInstance(myUpdate.ClassType);

    var lambda = myUpdate.MemberExpression as LambdaExpression;
    var memberExpr = lambda.Body as MemberExpression;
    var prop = memberExpr.Member as PropertyInfo;

    prop.SetValue(item, myUpdate.NewValue);

    item.Dump();
}

This properly outputs a DateTime value corresponding to when it was created without an exception.

like image 128
David L Avatar answered Sep 27 '22 20:09

David L


Maybe you can use a generic class?

class Update<TField>
{
  public Expression<Func<MyType, TField>> MemberExpression { get; set; }
  public TField NewValue { get; set; }
}

Not sure if you can just use a plain Func<MyType, TField> delegate instead of the expression tree, for your use.

like image 21
Jeppe Stig Nielsen Avatar answered Sep 27 '22 19:09

Jeppe Stig Nielsen