为啥连续抛出 2 个异常不会产生无法访问的代码警告?

Posted

技术标签:

【中文标题】为啥连续抛出 2 个异常不会产生无法访问的代码警告?【英文标题】:Why does throwing 2 exceptions in a row not generate an unreachable code warning?为什么连续抛出 2 个异常不会产生无法访问的代码警告? 【发布时间】:2011-06-16 12:11:22 【问题描述】:

为什么以下代码行不会产生编译器警告?

void Main()

  throw new Exception();
  throw new Exception();

在我看来,编译器应该会通知你无法达到第二个抛出异常。

【问题讨论】:

(我注意到 ReSharper 将第二个 throw 标记为无法访问) @Dan Eric Lippert 之前说过 he's not kibo (in the comments) - 如果您想提醒他感兴趣的问题,请通过他的博客联系他。 @AakashM 我真的不希望他回答,这更像是一个轻松的评论。虽然他可能是这里唯一一个可以给出明确答案的人。 【参考方案1】:

这显然是一个编译器错误,它是在 C# 3.0 中引入的——就在我大量重构可达性检查器的时候。这可能是我的错,对不起。

这个错误是完全良性的;基本上,我们只是在警告记者中忘记了一个案例。我们正确生成可达性信息;正如其他人所指出的,我们在 codegen 之前正确地修剪了无法访问的代码。

该错误只不过是警告生成器中的一个缺失案例。我们那里有一些棘手的代码,可确保当您使大部分代码无法访问时,我们不会报告无数警告。编译器具有专门报告无条件 goto(“goto”、“break”、“continue”)、条件 goto(“if”、“while”等)、try-catch-finally(包括等价形式)的代码try-catch-finally,如 lock 和 using)、块、返回(yield 返回和常规返回)、局部声明、标记语句、开关和表达式语句。

您在该列表中看到“抛出语句”吗?我也不。那是因为我们忘记了。

对给您带来的不便深表歉意。我会向 QA 发送一条说明,我们会在该语言的未来版本中对此进行修复。

感谢您提请我注意。

【讨论】:

感谢埃里克的解释。在我们的一位开发人员不小心复制了“throw new Exception()”行代码之后,我只是出于好奇才问到,但项目仍然构建(打开警告作为错误)。 @DaveShaw 这个错误有奇怪的后果,我在我的新“答案”中详细阐述了一点。【参考方案2】:

它可能会给出编译器警告/错误,但遗憾的是它没有。但是,如果您查看 IL 代码,则只考虑第一个异常。您可以登录 connect.microsoft.com 并提出您希望看到的内容。

如果你 ILDasm 下面的代码

static void Main(string[] args)
        
            Console.Write("Line 1");
           throw new Exception(); 
           throw new Exception();
           Console.Write("Line 4");
        

你会得到这个

.method private hidebysig static void  Main(string[] args) cil managed

  .entrypoint
  // Code size       18 (0x12)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Line 1"
  IL_0006:  call       void [mscorlib]System.Console::Write(string)
  IL_000b:  nop
  IL_000c:  newobj     instance void [mscorlib]System.Exception::.ctor()
  IL_0011:  throw
 // end of method Program::Main

在第一个 Exception 对象之后,没有其他任何东西被转换为 IL。

【讨论】:

【参考方案3】:

这个错误(正如 Lippert 上面所说的)会产生一些奇怪的后果。当然,这样的代码也不会给出编译时警告:

static int Main()

  return 0;

  throw new Exception("Can you reach me?");

如果您有创意,您仍然可以让throw 语句引发(不相关的)警告。在这个奇怪的例子中,代码生成警告只是因为“绿色”不可达:

static int Main()

  return 0;

  throw new Exception(((Func<string>)(() =>  if (2 == 2)  return "yellow";  return "green"; ))());

(代码只是从 lambda 创建一个委托实例并调用该委托)。

但这个例子更简单,看起来更糟:

static int Main()

  int neverAssigned;

  return 0;

  throw new Exception(neverAssigned.ToString());

最后一个代码示例也可以在没有警告的情况下编译! “使用”neverAssigned 没有问题,因为“使用”无法访问。但是您也不会收到关于从未分配给(也从未“真正”读取)的局部变量的警告。所以重复一遍,根本没有警告,这似乎是非常错误的。

我想知道在 Visual C# 的未来版本中是否会更改此行为?更改它会给人们带来以前没有的警告(我认为这是他们应得的)。

补充:Visual Studio 2015 的基于 Roslyn 的 C# 6.0 编译器似乎没有改变此行为。

【讨论】:

以上是关于为啥连续抛出 2 个异常不会产生无法访问的代码警告?的主要内容,如果未能解决你的问题,请参考以下文章

如果实体中没有getter和setter,为啥hibernate不会抛出异常

为啥在可变字典上调用的 setValue:forKeyPath 不会为未知键路径抛出异常?

为啥 GCC 不对无法访问的代码发出警告?

为啥geoip会抛出异常?

为啥抛出异常比返回错误代码更好?

为啥不可能从 __toString() 抛出异常?