C# 表达式之间的显式装箱无法正常工作。是否转换?

C# 表达式之间的显式装箱无法正常工作。是否转换?,c#,expression-trees,boxing,C#,Expression Trees,Boxing,最近,我在开发自制的SQLite ORM时遇到了一些关于使用表达式树进行装箱的问题。我还在重新编写C#3.5 长话短说,我将使用以下简单的类定义: [表格] 公共课 { [列(true),PrimaryKey] 公共UInt32 A{get;set;} [专栏] 公共字符串B{get;set;} } 因此,根据POCO类定义,我实例化了一个新对象,我的ORM引擎将该对象转换为记录,并正确插入。现在的技巧是,当我从SQLite返回值时,我得到了一个Int64 我认为我的委托设置器是可以的,因为它

最近,我在开发自制的SQLite ORM时遇到了一些关于使用表达式树进行装箱的问题。我还在重新编写C#3.5

长话短说,我将使用以下简单的类定义:

[表格]
公共课
{
[列(true),PrimaryKey]
公共UInt32 A{get;set;}
[专栏]
公共字符串B{get;set;}
}
因此,根据POCO类定义,我实例化了一个新对象,我的ORM引擎将该对象转换为记录,并正确插入。现在的技巧是,当我从SQLite返回值时,我得到了一个
Int64

我认为我的委托设置器是可以的,因为它是一个
操作
,但我仍然得到了
无效的异常
。似乎试图将
对象
(参数,
Int64
)强制转换为
UInt32
。不幸的是,它不起作用。我试图添加一些
表达式.Constant
表达式.TypeAs
(这对值类型的对象没有真正的帮助)

所以我想知道我这一代的二传手到底出了什么问题

下面是我的setter生成方法:

公共静态操作生成器设置(PropertyInfo PropertyInfo)
{
如果(!FactoryFastProperties.CacheSetters.ContainsKey(propertyInfo))
{
MethodInfo methodInfoSetter=propertyInfo.GetSetMethod();
ParameterExpression parameterExpressionInstance=Expression.Parameter(FactoryFastProperties.TypeObject,“实例”);
ParameterExpression parameterExpressionValue=表达式.参数(FactoryFastProperties.TypeObject,“值”);
UnaryExpression unaryExpressionInstance=Expression.Convert(参数ExpressionInstance,propertyInfo.DeclaringType);
UnaryExpression unaryExpressionValue=Expression.Convert(参数ExpressionValue,propertyInfo.PropertyType);
MethodCallExpression MethodCallExpression=Expression.Call(unaryExpressionInstance、methodInfoSetter、unaryExpressionValue);
表达式expressionActionObjectObject=Expression.Lambda(methodCallExpression,新参数Expression[]{parameterExpressionInstance,parameterExpressionValue});
FactoryFastProperties.CacheSetters.Add(propertyInfo,expressionActionObjectObject.Compile());
}
返回FactoryFastProperties.CacheSetters[propertyInfo];
}
所以基本上:

我可能应该添加另一个
表达式。Convert
,但我真的不知道这对它的工作有什么帮助。因为已经有一个应该(尝试)从任何
对象
转换为属性类型(在我的示例中是
UInt32
类型)


有没有办法解决这个问题?

对于这个答案,假设定义如下:

object myColValueFromTheDatabase = (object)64L;
Expression.Convert
静态确定如何执行转换。就像C#一样。如果您从数据库中写入
(uint)myCLVALUE
,这在运行时将不会成功,因为取消装箱不起作用
Expression.Convert
也进行简单的取消装箱尝试。这就是它失败的原因

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

  • (uint)(long)MyCalValue来自数据库
  • Convert.ToUInt32(MyCalValue来自数据库)
  • 在第(1)种情况下,您需要首先取消装箱到精确的匹配类型,然后更改位。案例(2)使用一些助手方法解决了这个问题。案例(1)更快

    要使用表达式API执行此操作,请插入另一个
    表达式。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);
        }
    
    公共静态T日志值(T val)
    {
    Console.WriteLine(val.GetType().Name+“:”+val);
    返回val;
    }
    静态void Main(字符串[]参数)
    {
    表达式myCalvalueFromtheDatabase=Expression.Convert(Expression.Constant(1234L),typeof(object));
    MyCalValueFromtheDatabase=Expression.Call(typeof(Program),“LogValue”,new[]{MyCalValueFromtheDatabase.Type},MyCalValueFromtheDatabase);//log
    Expression unbox=Expression.Convert(MyCalvaluefromDatabase,typeof(long));
    Expression converted=Expression.Convert(unbox,typeof(uint));
    var result=Expression.Lambda(已转换).Compile();
    控制台写入线(结果);
    }
    
    表达式。ConvertChecked
    更安全吗…?@DavidHaney更安全对你来说意味着什么?它执行运行时范围检查,但没有解决此问题。我尝试添加另一个表达式。转换如下:UnaryExpression unaryExpressionProperty=Expression.Convert(parameterExpressionProperty,parameterExpressionProperty.Type.UnderlineingSystemType);unaryExpressionProperty=Expression.Convert(unaryExpressionProperty,typeProperty);但还是有例外,我真的不知道为什么。parameterExpression的类型应该是Int64,就像在运行时一样,然后强制转换为属性的类型(UInt32),所以从技术上讲,我是这样包装的:(UInt32)(Int64)MyCalvalueFromtheDatabase有什么错误吗?这些表达式让我有点困惑。我在答案中添加了一些代码,以便我们可以讨论一些可执行的东西。请注意用于调试表达式树中的运行时值的函数LogValue。您可以使用它来调试取消装箱和强制转换,以确保运行时类型符合预期。我怀疑某些值在您的程序中有意外类型。是的,如果静态地知道所有类型,请不要使用
    Convert.ChangeType
    。请注意,在
    Expression.Convert(parameterExpressionValue,propertyInfo.PropertyType)
    中,
    parameterExpressionValue
    的运行时值必须为
    propertyInfo.PropertyType
    类型,才能进行解装箱。如果需要处理动态类型,则不能使用静态unbox。记住,
    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);
        }