Expression.Convert不会为不变值类型参数抛出InvalidOperationException?

时间:2011-10-31 21:34:14

标签: c# expression-trees covariance contravariance

“在Expression.Type和type之间没有定义转换运算符时,

Expression.Convert通常会引发InvalidOperationException

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

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

isn't covariant for value types

  

差异仅适用于参考类型;如果为变量类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

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

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

  1. 这是一个错误吗? (I reported it as a bug on Microsoft Connect。)
  2. 如何正确检查类型是否可以转换为其他类型? Some answers似乎是指使用Convert。我非常喜欢一种不必将异常处理用作逻辑的方法。

  3. 似乎没有正确支持整个方差逻辑。它正确地抱怨无法从Func<SomeType>转换为Func<SomeOtherType>,但它没有抱怨从Func<object>转换为Func<string>

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

3 个答案:

答案 0 :(得分:7)

  

这是一个错误吗?

是。当我们添加协方差和逆变时,表达式树库可能不会始终更新。对不起。

  

我将其报告为Microsoft Connect上的错误。

谢谢!有人会看看它。

  

如何正确检查某种类型是否可以转换为其他类型?

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

  • .NET运行时是否认为类型是赋值兼容的?
  • C#编译器是否认为类型之间存在隐式转换?
  • C#编译器是否认为类型之间存在显式转换?
例如,“int”和“short”与.NET规则不兼容。 Int是显式可转换但不能隐式转换为short,而short可以通过C#规则隐式和显式转换为int。

答案 1 :(得分:1)

这不是一个错误。 Expression.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。它似乎没有检查协变(返回类型)参数。所以我开始怀疑这个一个错误,虽然我倾向于保留对此的判断,直到我有机会查看规范(参见Eric Lippert的Maybe there's something wrong with the universe, but probably not),我现在没有时间做。

答案 2 :(得分:0)

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

  

如何正确检查某种类型是否可以转换为其他类型?

我只是首次尝试使用Type.CanConvertTo( Type to )方法to my library。 (来源太复杂了,不能在这里发帖,抱歉。)

if ( fromType.CanConvertTo( toType ) )
{
    convertedExpression = Expression.Convert( expression, toType );
}

到目前为止,它支持检查隐式转化

  • 简单的非泛型类型。
  • 嵌套通用接口和委托的差异。
  • 通用值类型参数的不变性。

它不支持:

  • 类型约束。
  • 自定义隐式转换运算符。

通过all my tests for implicit conversions。虽然您也可以指定包含显式转换(我实际上是use it like that at the moment),但我仍然需要为所有这些场景编写单元测试。