C#编译器如何优化异常过滤器?

4
在C# 6中出现了异常过滤器。因此,我们可以编写一些重试逻辑,例如:
public static void Retry()
    {
        int i = 3;
        do
        {
            try
            {
                throw new Exception();
            }
            catch (Exception) when (--i < 0)
            {
                throw;
            }
            catch (Exception)
            {
                Thread.Sleep(10);
            }
        } while (true);
    }

在控制台应用程序中它运行得很好。 但是,如果我们创建一个带有“优化代码”选项的网站应用程序,会出现无限循环,因为“i”的值永远不会改变。如果没有“优化代码”,这将按预期工作。 如何测试: 在空的asp.net网站应用程序中创建(我尝试了.net 4.5.2和.net 4.6)。将此代码添加到全局应用程序类

public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        int i = 3;
        do
        {
            try
            {
                throw new Exception();
            }
            catch (Exception) when (--i < 0)
            {
                throw;
            }
            catch (Exception)
            {
                Thread.Sleep(10);
            }
        } while (true);
    }
}

项目属性 -> 构建 -> 勾选“优化代码”。运行应用程序。得到无限循环。

这是正确的行为还是编译器中的错误?请注意,不要删除 HTML 标签。

更新1:似乎是一种非常罕见的情况,与单个递减和重新抛出异常有关。在 Windows 7 上使用 VS 2015 编译时会重现此问题(尝试在几台计算机上进行操作)。但在 Windows 10 上使用 VS2015 就没有问题。
如果将代码更改如下,则也可以正常工作。

int i = 3;
   do
   {
       try
       {
          throw new Exception();
       }
       catch (Exception) when (--i > 0)
       {
          Thread.Sleep(10);
       }
   } while (true);

哪个在实际应用中更合适(因为堆栈展开)


3
我刚刚在 VS 2015 中新建的 ASP.NET Web Forms 项目中尝试了一下,一切都按预期进行。看起来你的特定配置受到了影响(或者在采用 SO 代码时,你意外删除了错误部分)。但无论如何,以这种方式使用过滤器都是个不好的主意。 - Timur Mannapov
你在发布配置中尝试过吗? - Oleg Alekseenko
创建一个测试项目(https://github.com/alekole/ExceptionFilterWrongBehaivior)。 - Oleg Alekseenko
是的,在发布版和调试版都可以正常工作。 - Timur Mannapov
2个回答

2
我认为这可能是一个bug。在我看来,你应该将其报告为bug。但无论如何,我不建议您采取这种方法。
首先,在异常过滤器中引入了副作用。一般情况下,这可能是一个不好的实践。从CQS的角度来看,过滤器是一个查询,而不是一个命令。
其次,你并没有真正获得任何好处。因为你在下一个块中捕获了相同的异常,那么除了将逻辑放入第二个catch块之外,异常过滤器行为(如果没有匹配,则不会进行堆栈展开)有什么好处呢?没有。
代码如下:
int i = 3;
do
{
  try
  {
    throw new Exception();
  }
  catch (Exception)
  {
    if (--i < 0)
      throw;
    Thread.Sleep(10);
  }
} while (true);

表达了这样一种想法:您总是希望捕获异常,但在面对捕获的异常时,根据其他条件采取不同的行为。这比异常过滤器更好地表达了重试概念,后者表达了只有在其他条件下才想要捕获异常的想法。


会同意你的答案,因为在最合适的使用情况下(请看我的更新),它是有效的。 - Oleg Alekseenko

0
一个快速的解决方法是将递减逻辑放在catch块内,并将计数器减1。
public static void Retry()
{
    int i = 3 - 1;
    do
    {
        try
        {
            throw new Exception();
        }
        catch (Exception) when (i < 0)
        {
            throw;
        }
        catch (Exception)
        {
            i--;
            Thread.Sleep(10);
        }
    } while (true);
}

好的,我可以说现在这是一个bug了。测试表明你的代码在32位模式下运行良好,但在64位模式下无法正常工作。


是的,我们可以。但我不明白为什么行为会在“优化代码”时发生变化。 - Oleg Alekseenko
好的。并不是所有东西都完美无缺。即使在 .Net 中 ;) 那可能是个错误。@OlegAlekseenko - M.kazem Akhgary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接