【问题标题】:Expression.Convert doesn't throw InvalidOperationException for invariant value type parameters?Expression.Convert 不会为不变值类型参数抛出 InvalidOperationException?
【发布时间】:2011-10-31 21:34:13
【问题描述】:

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

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

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

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

有趣的是,一旦SomeTypeSomeOtherType 在同一个类层次结构中(SomeOtherType 扩展自SomeType),它就永远不会抛出异常。如果不是,那就是。

【问题讨论】:

    标签: c# expression-trees covariance contravariance


    【解决方案1】:

    这是一个错误吗?

    是的。当我们添加协变和逆变时,表达式树库可能没有始终如一地更新。对此感到抱歉。

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

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

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

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

    • .NET 运行时是否认为类型与赋值兼容?
    • C# 编译器是否认为类型之间存在隐式转换?
    • C# 编译器是否认为类型之间存在显式转换?

    “int”和“short”例如与 .NET 规则不兼容的赋值。根据 C# 规则,Int 可以显式转换但不能隐式转换为 short,并且 short 可以隐式和显式转换为 int。

    【讨论】:

    • “给定两个type对象,你想知道吗”:如msdn documentation on Expression.Convert“没有定义表达式.Type和type之间的转换运算符." 现在我无法知道我的表达式树是否代表有效代码,直到运行它。出于我的目的,我需要知道是否可以进行隐式转换。
    【解决方案2】:

    这不是错误。 Expression.Convert 表示运行时类型检查,因此运行时的 InvalidCastException 将是预期的行为。

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

    编辑:我也在使用 .NET 4.0。

    顺便说一句,Convert 不会抱怨从 Func&lt;object&gt; 转换为 Func&lt;string&gt; 因为这种转换有时是合法的。如果Func&lt;object&gt; 是对运行时类型为Func&lt;string&gt; 的对象的协变引用,则它是合法的。示例:

    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),我现在没时间做。

    【讨论】:

    • 没错,所以它会知道它不能将Func&lt;int&gt; 转换为Func&lt;object&gt;?例如。它知道它不能将Func&lt;object&gt; 转换为Func&lt;string&gt;。编辑:所以它似乎也不知道。 :O
    • @StevenJeuris 没错。关键是无论 Convert 知道什么,它只能在运行时知道。编译器不进行静态分析。因此,您可以使用各种非法转换来调用 Convert,并且调用将编译。在代码运行之前,它们不会失败。
    • 这就是我投反对票的原因,这不是真的。试试看。其他 SE 问题实际上是指使用异常 Convert throws 作为检查到给定类型的转换是否会成功的一种方式。 (抱歉,无法立即找到它。)此外,它似乎甚至对泛型类型进行某种形式的类型参数检查,因为我的更新中指定不允许 Func&lt;SomeType&gt;Func&lt;SomeOtherType&gt;
    • @StevenJeuris 你的评论让我很困惑:Func&lt;string&gt; 可转换为Func&lt;object&gt;; Func&lt;object&gt;不能转换为Func&lt;string&gt;。但是,通过 Expression.Convert 影响每个转换确实可以编译。
    • 他不是要求编译器错误;他在编译表达式而不是运行它时要求例外。
    【解决方案3】:

    Eric Lippert 回答了我的问题的第 1 部分:这似乎是一个错误。我开始寻找问题 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),但我仍然需要为所有这些场景编写单元测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      相关资源
      最近更新 更多