Unboxing NULL Types - Workaround?

I save the update operation as follows:

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

For instance:

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

Then I try 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 assign a nullable value to an object it either becomes null or enters a value type.

Since (DateTime?)myUpdate.Value will work to return it to the correct type, I tried to simulate this compile-time construct using Convert.ChangeType . However, it says casting from DateTime to DateTime? impossible.

What approach can I use here to return an object to its original type?

Is there a way to store types with zero value, regular structures and regular objects in one field and return exact data to it?

+6
source share
3 answers

If you know the type of your expression when creating myUpdate, SetInfo() will correctly set your data, even if boxing / unpacking.

 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 correctly displays the DateTime value corresponding to when it was created without exception.

+1
source

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 the simple Func<MyType, TField> delegate instead of the expression tree for your use.

+1
source

I was able to solve using expressions and without reflection:

  var lambda = update.MemberExpression as LambdaExpression; var memberExpr = lambda.Body as MemberExpression; if (memberExpr == null) { throw new NotSupportedException("Field expression must be a member expression"); } // do a cast - this ensures nullable types work, for instance var cast = Expression.Convert(Expression.Constant(update.Value), update.FieldType); // assign the target member with the cast value var assignment = Expression.Assign(memberExpr, cast); // build a new lambda, no return type, which does the assignment var newLambda = Expression.Lambda(typeof(Action<T>), assignment, lambda.Parameters[0]); // compile to something we can invoke, and invoke var compiled = (Action<T>)newLambda.Compile(); compiled(item); 

And voila, the item is changed :)

0
source

Source: https://habr.com/ru/post/987769/


All Articles