表达式.Convert不会为不变值类型参数抛出InvalidOperationException

本文关键字:类型参数 InvalidOperationException Convert 表达式 | 更新日期: 2023-09-27 18:12:50

Expression.Convert通常在"表达式之间没有定义转换操作符时抛出InvalidOperationException。打字和打字。"

对于引用类型,Func<>的返回类型参数是协变的。

// This works.
Func<SomeType> a = () => new SomeType();
Func<object> b = a;

对于值类型它不是协变的

方差只适用于引用类型;如果为变体类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

// This doesn't work!
Func<int> five = () => 5;
Func<object> fiveCovariant = five;

然而,Expression.Convert相信这是可能的。

Func<int> answer = () => 42;
Expression answerExpression = Expression.Constant( answer );
// No InvalidOperationException is thrown at this line.
Expression converted 
    = Expression.Convert( answerExpression, typeof( Func<object> ) );

调用Expression.Convert时没有抛出InvalidOperationException。表达式树编译正确,但是当我调用创建的委托时,我得到了预期的InvalidCastException

    这是一个bug吗?(我在Microsoft Connect上报告了一个bug)
  1. 如何正确检查一种类型是否可以转换为另一种类型?一些答案似乎是指使用Convert。我非常喜欢不需要使用异常处理作为逻辑的方法。

似乎整个方差逻辑没有得到正确支持。它正确地抱怨不能从Func<SomeType>转换到Func<SomeOtherType>,但它没有抱怨从Func<object>转换到Func<string>

有趣的是,一旦SomeTypeSomeOtherType在同一个类层次结构中(SomeOtherTypeSomeType扩展),它就不会抛出异常。

表达式.Convert不会为不变值类型参数抛出InvalidOperationException

这是一个bug吗?

是的。当我们添加协方差和逆变时,表达式树库可能没有持续更新。很抱歉。

我把它报告为Microsoft Connect的bug。

谢谢!到时候会有人来看看的。

如何正确检查一个类型是否可以转换为另一个类型?

这个问题很模糊。给定两个类型对象,您想知道:

  • .NET运行时认为类型是赋值兼容的吗?
  • c#编译器认为类型之间存在隐式转换吗?c#编译器认为类型之间有显式转换吗?
例如,

"int"answers"short"在。net规则中不兼容赋值。Int可以显式转换,但不能隐式转换为short,根据c#规则,short可以隐式和显式转换为Int。

这不是bug。表达式。Convert表示运行时类型检查,因此运行时的InvalidCastException将是预期的行为。

编辑:这并不完全正确。它并不完全代表运行时类型检查(这里是文档:http://msdn.microsoft.com/en-us/library/bb292051.aspx)。然而,表达式树是在运行时创建的,所以所有的类型检查都必须在运行时进行。

编辑:我也在用。net 4.0。

顺便说一下,Convert没有抱怨从Func<object>转换到Func<string> ,因为这种转换有时是合法的。如果Func<object>是对运行时类型为Func<string>的对象的协变引用,则它是合法的。例子:
Func<string> sFunc = () => "S";
Func<object> oFunc = sFunc;
Func<string> anotherSFunc = (Func<string>)oFunc;

现在,Convert通过检查一种类型是否可以强制转换为另一种类型来决定是否抛出InvalidOperationException。在检查委托是否存在潜在的引用转换时,看起来代码确实检查了逆变(实参)形参,如果有值类型,则抛出InvalidOperationException。它似乎没有检查协变(返回类型)参数。所以我开始怀疑这个一个bug,尽管我倾向于保留判断,直到我有机会看看规范(见Eric Lippert的"也许宇宙有问题,但也可能没有"),我现在没有时间做。

Eric Lippert回答了我问题的第一部分:这似乎是一个bug。我开始寻找问题2的解决方案:

如何正确检查一个类型是否可以转换为另一个类型?

我刚刚向我的库提交了Type.CanConvertTo( Type to )方法的第一次尝试。(来源太复杂了,不好意思在这里发布)

if ( fromType.CanConvertTo( toType ) )
{
    convertedExpression = Expression.Convert( expression, toType );
}
到目前为止,它支持检查隐式转换
  • 简单非泛型类型
  • 嵌套的泛型接口和委托的方差
  • 泛型值类型参数的不变性。

不支持:

  • 类型约束。
  • 自定义隐式转换操作符。

它通过了所有隐式转换的测试。尽管您也可以指定包括显式转换(实际上我现在就是这样使用它的),但我仍然需要为所有这些场景编写单元测试。