gpt4 book ai didi

c# - try catch block 中的 "when"关键字是否与 if 语句相同?

转载 作者:行者123 更新时间:2023-12-02 03:09:11 25 4
gpt4 key购买 nike

在 C# 6.0 中引入了“when”关键字,现在您可以在 catch 块中过滤异常。但这与 catch 块中的 if 语句不一样吗?如果是这样,它不只是语法糖还是我遗漏了什么?

例如,带有“when”关键字的 try catch 块:

try { … } 
catch (WebException ex) when ex.Status == WebExceptionStatus.Timeout {
//do something
}
catch (WebException ex) when ex.Status== WebExceptionStatus.SendFailure {
//do something
}
catch (Exception caught) {…}

或者
try { … } 
catch (WebException ex) {
if(ex.Status == WebExceptionStatus.Timeout) {
//do something
}
}
catch (WebException ex) {
if(ex.Status == WebExceptionStatus.SendFailure) {
//do something
}
}
catch (Exception caught) {…}

最佳答案

除了这里已经有的几个很好的答案之外:异常过滤器和 catch 块中的“if”之间还有一个非常重要的区别:过滤器在内部 finally 块之前运行 .

考虑以下:

void M1()
{
try { N(); } catch (MyException) { if (F()) C(); }
}
void M2()
{
try { N(); } catch (MyException) when F() { C(); }
}
void N()
{
try { MakeAMess(); DoSomethingDangerous(); }
finally { CleanItUp(); }
}

M1 和 M2 的调用顺序不同 .

假设 M1 被调用。它调用 N(),后者调用 MakeAMess()。弄得一团糟。然后 DoSomethingDangerous() 抛出 MyException。运行时检查是否有任何可以处理的 catch 块,并且有。 finally 块运行 CleanItUp()。乱七八糟的被清理了。控制传递到 catch 块。然后 catch 块调用 F(),然后,也许,C()。

M2呢?它调用 N(),后者调用 MakeAMess()。弄得一团糟。然后 DoSomethingDangerous() 抛出 MyException。运行时会检查是否有任何可以处理的 catch 块,并且可能有。运行时调用 F() 以查看 catch 块是否可以处理它,它可以。 finally 块运行 CleanItUp(),控制传递给 catch,并调用 C()。

你注意到区别了吗?在 M1 的情况下,F() 在清理完困惑之后调用,而在 M2 的情况下,它在清理困惑之前调用。如果 F() 依赖于它的正确性没有困惑,那么如果你重构 M1 看起来像 M2,你就会遇到大麻烦!

这里不仅仅是正确性问题;也存在安全隐患。假设我们制造的“困惑”是“冒充管理员”,危险操作需要管理员访问权限,而清理则不冒充管理员。在 M2 中,对 F 的调用具有管理员权限。在 M1 中它没有。假设用户对包含 M2 的程序集授予了很少的权限,但 N 在一个完全信任的程序集中; M2 程序集中的潜在敌对代码可以通过这种引诱攻击获得管理员访问权限。

作为练习:您将如何编写 N 以防御这种攻击?

(当然,运行时足够聪明,可以知道是否存在授予或拒绝 M2 和 N 之间权限的堆栈注释,并在调用 F 之前恢复这些注释。运行时造成的困惑,它知道如何正确处理它。但是运行时不知道您造成的任何其他困惑。)

这里的关键要点是,任何时候您处理异常时,根据定义,都会出现可怕的错误,世界并不像您认为的那样。异常过滤器的正确性不得依赖于已被异常条件违反的不变量。

更新:

伊恩·林罗斯(Ian Ringrose)问我们是如何陷入这种困惑的。

这部分答案可能有点猜想,因为这里描述的一些设计决策是在我 2012 年离开 Microsoft 后进行的。但是我已经多次与语言设计人员讨论这些问题,我想我可以给出一个公正的总结情况。

在 CLR 的早期就做出了在 finally 块之前运行过滤器的设计决策;询问您是否想要该设计决策的小细节的人是 Chris Brumme。 (更新:遗憾的是,Chris 不再可以提问了。)他曾经有一个博客,其中详细解释了异常处理模型,但我不知道它是否还在互联网上。

这是一个合理的决定。出于调试目的,我们想在 finally 块运行之前知道是否要处理此异常,或者我们是否处于完全未处理的异常破坏进程的“未定义行为”场景中。因为如果程序在调试器中运行,未定义的行为将包括在 finally 块运行之前在未处理的异常点处中断。

CLR 团队很好地理解了这些语义引入安全性和正确性问题的事实;事实上,我在我的第一本书中讨论了它,这本书很多年前就已经发行了,十二年前在我的博客上:

https://blogs.msdn.microsoft.com/ericlippert/2004/09/01/finally-does-not-mean-immediately/

即使 CLR 团队想要,现在“修复”语义也将是一个突破性的变化。

该特性在CIL和VB.NET中一直存在,攻击者通过过滤器控制代码的实现语言,因此将该特性引入C#并不会增加任何新的攻击面。

事实上,这个引入安全问题的功能已经“流行”了几十年,据我所知,它从来都不是导致严重安全问题的原因,这证明它对攻击者来说不是一个很有成效的途径。

那么为什么该功能在 VB.NET 的第一个版本中出现并花费了十多年时间才将其纳入 C#?嗯,像这样的“为什么不”问题很难回答,但在这种情况下,我可以很容易地总结:(1)我们有很多其他的想法,(2)安德斯认为这个功能没有吸引力。 (而且我对它也不感到兴奋。)这使它多年来一直处于优先级列表的底部。

那么它是如何使其在 C# 6 中实现的优先级列表中足够高的呢?许多人要求使用此功能,这始终是赞成这样做的。 VB 已经拥有它,并且 C# 和 VB 团队希望尽可能以合理的成本实现对等,所以这也是重点。但最大的转折点是:在 Roslyn 项目本身的一个场景中,异常过滤器会非常有用。 (我不记得它是什么了;如果你想找到它并返返回告,请深入研究源代码!)

作为语言设计者和编译器编写者,您要小心不要优先考虑那些使编译器编写者的生活更轻松的功能;大多数 C# 用户不是编译器编写者,他们是客户!但最终,拥有该功能有用的真实场景集合,包括一些激怒编译器团队本身的场景,打破了平衡。

关于c# - try catch block 中的 "when"关键字是否与 if 语句相同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39506949/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com