明确的拳击之间与Expression.Convert无法正常工作?

时间:2014-01-04 19:15:55

标签: c# expression-trees boxing

最近,当我开发自制的SQLite ORM时,我遇到了一些关于使用Expression Trees进行拳击的麻烦。我还在编码C#3.5。

总而言之,我将使用这个简单的类定义:

[Table]
public class Michelle
{
    [Column(true), PrimaryKey]
    public UInt32 A { get; set; }

    [Column]
    public String B { get; set; }
}

所以从POCO类定义中我实例化了一个新对象,我的ORM引擎将该对象转换为记录并正确插入。现在的诀窍是,当我从SQLite中获取值时,我得到了Int64

我认为我的委托制定者是好的,因为它是Action<Object, Object>,但我仍然得到InvalidCastException。似乎尝试将Object(参数,Int64)强制转换为UInt32。不幸的是它不起作用。我尝试添加一些Expression.ConstantExpression.TypeAs(这对值类型对象没有帮助)。

所以我想知道我的二传手生成中出了什么问题。

以下是我的setter生成方法:

public static Action<Object, Object> GenerateSetter(PropertyInfo propertyInfo)
{
    if (!FactoryFastProperties.CacheSetters.ContainsKey(propertyInfo))
    {
        MethodInfo methodInfoSetter = propertyInfo.GetSetMethod();
        ParameterExpression parameterExpressionInstance = Expression.Parameter(FactoryFastProperties.TypeObject, "Instance");
        ParameterExpression parameterExpressionValue = Expression.Parameter(FactoryFastProperties.TypeObject, "Value");

        UnaryExpression unaryExpressionInstance = Expression.Convert(parameterExpressionInstance, propertyInfo.DeclaringType);
        UnaryExpression unaryExpressionValue = Expression.Convert(parameterExpressionValue, propertyInfo.PropertyType);

        MethodCallExpression methodCallExpression = Expression.Call(unaryExpressionInstance, methodInfoSetter, unaryExpressionValue);

        Expression<Action<Object, Object>> expressionActionObjectObject =  Expression.Lambda<Action<Object, Object>>(methodCallExpression, new ParameterExpression[] { parameterExpressionInstance, parameterExpressionValue });

        FactoryFastProperties.CacheSetters.Add(propertyInfo, expressionActionObjectObject.Compile());
    }

    return FactoryFastProperties.CacheSetters[propertyInfo];
}

所以基本上:

// Considering setter as something returned by the generator described above
// So:

// That one works!
setter(instance, 32u);

// This one... hm not really =/
setter(instance, 64);

我应该添加另一个Expression.Convert,但我真的不知道如何使它工作。因为已经有人应该(尝试)从任何Object转换为属性类型(在我的示例中为UInt32类型)。

有什么想法解决这个问题吗?

1 个答案:

答案 0 :(得分:3)

对于这个答案,假设定义了以下内容:

object myColValueFromTheDatabase = (object)64L;

Expression.Convert静态确定如何执行转换。就像C#一样。如果您编写(uint)myColValueFromTheDatabase,这将无法在运行时成功,因为取消装箱只是不起作用。 Expression.Convert也进行了简单的拆箱尝试。这就是它失败的原因。

您需要执行以下任一操作:

  1. (uint)(long)myColValueFromTheDatabase
  2. Convert.ToUInt32(myColValueFromTheDatabase)
  3. 在情况(1)中,您需要首先取消装箱到完全匹配类型,然后更改位。案例(2)使用一些辅助方法解决了这个问题。案例(1)更快。

    要使用表达式API执行此操作,请插入另一个Expression.Convert


    这应该让你开始:

        public static T LogValue<T>(T val)
        {
            Console.WriteLine(val.GetType().Name + ": " + val);
            return val;
        }
        static void Main(string[] args)
        {
            Expression myColValueFromTheDatabase = Expression.Convert(Expression.Constant(1234L), typeof(object));
            myColValueFromTheDatabase = Expression.Call(typeof(Program), "LogValue", new[] { myColValueFromTheDatabase.Type }, myColValueFromTheDatabase); //log
            Expression unboxed = Expression.Convert(myColValueFromTheDatabase, typeof(long));
            Expression converted = Expression.Convert(unboxed, typeof(uint));
    
            var result = Expression.Lambda<Func<uint>>(converted).Compile()();
            Console.WriteLine(result);
        }