取消装箱可空类型 - 解决方法?

时间:2015-05-22 19:11:07

标签: c#

我正在存储更新操作:

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

例如:

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

然后我试图稍后(简化)应用此更新:

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

但是,myUpdate.ValueDateTime,而不是DateTime?。这是因为当您将可归结为object时,它会变为null或将值类型化为空。

由于(DateTime?)myUpdate.Value可以使其恢复到正确的类型,我尝试使用Convert.ChangeType来模拟这个编译时构造。但是,它表示无法从DateTime投射到DateTime?

我可以在这里使用什么方法将对象恢复为原始类型?

有没有办法在一个字段中存储可以为空的类型,常规结构和常规对象,然后再将相同的内容存储回来?

3 个答案:

答案 0 :(得分:1)

也许你可以使用泛型类?

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

不确定是否可以使用普通Func<MyType, TField>委托代替表达式树,供您使用。

答案 1 :(得分:1)

如果您在创建myUpdate时知道Expression的类型,SetInfo()将正确设置您的数据,即使装箱/取消装箱也是如此。

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

这正确输出了一个DateTime值,该值对应于创建它时没有例外。

答案 2 :(得分:0)

我能够解决使用表达式而且没有反射:

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

瞧,该项目已被修改:)