【问题标题】:Dynamic variable in throw statement fails with CS0155throw 语句中的动态变量因 CS0155 而失败
【发布时间】:2016-10-14 02:59:40
【问题描述】:

我在重构一些代码时遇到了一个奇怪的错误 (?)。

由于某种未知原因,在 throw 语句中将动态变量传递给静态方法时出现编译器错误。

dynamic result = comObj.getfoo(bar);

// ....

throw ApiException.FromResult(result);

错误 CS0155
捕获或抛出的类型必须派生自 System.Exception

这是没有意义的,因为返回值 派生自 System.Exception

此外,让我倾向于这是一个错误的原因是,该错误可以通过以下任一方式规避:

  1. dynamic 更改为var

  var result = 0;
  1. 通过将变量转换为object

  throw ApiException.FromResult((object)result);

有什么想法吗?


Live on dotnetfiddle

/* PS: code omitted for brevity */

using System;

public class Program
{

    public class ApiException : Exception
    {

        public static ApiException FromResult(object result)
        {
            return new ApiException();
        }

    }

    [STAThread]
    public static void Main(string[] args)
    {       
        dynamic result = 0;
        throw ApiException.FromResult(result);  
    }

}

【问题讨论】:

  • Roslyn 和旧编译器都会出现这种情况。
  • @IanMundy:不;这是一个常规的静态方法。
  • @SLaks 正确,我用两种编译器进行了测试。那么,是错误还是设计?
  • 如果resultdynamic,那么表达式ApiException.FromResult(result) 也是dynamic,它不是从Exception 派生的。

标签: c#


【解决方案1】:

这是“预期的”行为,因为任何包含dynamic 的语句都被视为其结果类型为dynamic,在编译时无法转换为Exception

dynamic x = new Exception();
throw x; // fails to compile as x's type 
         // is not known to derive from `Exception` at compile time

throw (Exception)x; // works

您的解决方法将编译时结果从 dynamic 更改为正确的类型,从而正确编译。

dynamic result =....
throw 
      // compile type here is "dynamic" as one parameter is `dynamic`
      ApiException.FromResult(result);

这是因为 var 不是 dynamic (What's the difference between dynamic(C# 4) and var?)

var result = ... // any non dynamic type - staticly know at compile time
throw 
      // compile type here is ApiException because type of parameter is known
      // at compile time 
      ApiException.FromResult(result);

当您尝试在 dynamic 结果 - Extension method and dynamic object 上调用扩展时会发生类似问题


为了解决“抛出”与函数调用不同的问题(在运行时愉快地处理解析):throw statement 的规范要求表达式类型为 Exception 不同于 表达式包括有动态绑定规则的方法调用(7.2. 静态和动态绑定)。

有趣的是,像 while 这样的其他语句在编译时对 dynamic 值很满意,我看不出它们行为不同的原因。

来自 C# 5 规范的精选

7.2 静态和动态绑定
...
当一个操作被动态绑定时,编译器很少或不执行检查。相反,如果运行时绑定失败,则会在运行时将错误报告为异常。
C# 中的以下操作需要绑定:
• 方法调用:e.M(e1,…,en)
...
8.9.5 throw 语句:
...
表达式必须表示类类型 System.Exception、派生自 System.Exception 的类类型或以 System.Exception(或其子类)作为其有效基类的类型参数类型的值。

【讨论】:

  • 就是这样。那么,我应该责怪IDE吗?当我将鼠标悬停在FromResult 时,它仍然显示为ApiException
  • @Bjørn-RogerKringsjå 当然......同样在这一点上,IDE 很难选择尝试通用并显示方法类型或与编译器 100% 兼容,这可能需要比智能感知更多的信息有那个时候。我相信 dynamic 的智能感知是最好的努力,因为一切都可能在运行时发生变化,所以我对它的期望不高。
  • "an object of type dynamic bypasses static type checking";这是否意味着动态绕过Exception 类型的编译时静态类型检查except?或者这些语句与“静态类型检查”不同:“// 编译失败,因为 x 的类型 // 不知道在编译时从 Exception 派生”?
  • @Quantic 我在帖子中添加了我对原因的理解 - throw 是具有特殊要求的声明,dynamic 没有特殊情况。
【解决方案2】:

涉及dynamic 的大多数操作都会导致dynamic 作为结果类型,因此您会看到错误。 (如其他 answer 中所述)

但并不是所有涉及dynamic 的操作都会产生动态类型,例如构造函数。如果你的类中有构造函数:

public ApiException(object result)
{

}

然后你尝试像这样调用它:

dynamic result = 0;
throw new ApiException(result);

不会有错误。

见:Using Type dynamic (C# Programming Guide)

大多数动态操作的结果本身就是动态的。

还有:

结果不是动态的操作包括从 动态到另一种类型,以及包含参数的构造函数调用 动态类型。

它与var 一起工作的原因是因为它在编译时是一个强类型变量。

【讨论】:

    猜你喜欢
    • 2013-01-15
    • 1970-01-01
    • 2020-11-03
    • 2017-06-14
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 2018-05-16
    • 2018-04-23
    相关资源
    最近更新 更多