编译器如何优化 C# 中的异常过滤器?
How compiler optimize exception filters in c#?
在 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);
}
在控制台应用程序中效果很好。
但是如果我们用 "optimize code" 创建网站应用程序就会出现无限循环,因为 'i' 的值永远不会改变。没有 "optimize code" 这按预期工作。
如何测试:
在空 asp.net 网站应用程序中创建(我尝试 .net 4.5.2 和 .net 4.6)。将此代码添加到全局应用程序 class
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);
}
}
项目属性 -> 构建 -> 检查 "optimize code"。 运行 申请。获取无限循环。
这是正确的行为还是编译器中的错误?
Upd1:
因此,unar 递减和重新抛出异常似乎是非常罕见的情况。
在 windows 7 上的 VS 2015 中编译时重复(在多台机器上尝试)。在 VS2015 中 windows 10 工作正常。
如果像这样更改代码也可以工作
int i = 3;
do
{
try
{
throw new Exception();
}
catch (Exception) when (--i > 0)
{
Thread.Sleep(10);
}
} while (true);
哪个更适合现实世界的例子(因为展开的堆栈)
快速修复。您可以将递减逻辑移到 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);
}
好的。我现在可以说这是一个错误。测试显示您的代码在 32 位模式下工作正常。但不适用于 64 位模式。
我认为这可能是一个错误。 IMO,你应该这样报告。不管是不是,虽然我不建议你采用这种方法。
首先,您在异常过滤器中产生了副作用。通常这可能是一种不好的做法。用 CQS 的术语来思考它;过滤器是查询,而不是命令。
其次,你并没有真正得到任何东西。因为您在下一个块中捕获了相同的异常,所以与仅将逻辑放入第二个 catch 块相比,您从异常过滤器行为(如果不匹配则不展开堆栈)中获得了什么?没有。
代码:
int i = 3;
do
{
try
{
throw new Exception();
}
catch (Exception)
{
if (--i < 0)
throw;
Thread.Sleep(10);
}
} while (true);
表示您总是想捕获异常,但您希望在捕获该异常时根据其他条件采取不同的行为。这使得它比异常过滤器更好地表达了重试概念,异常过滤器表达了您只想捕获异常的想法,具体取决于其他条件。
在 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);
}
在控制台应用程序中效果很好。 但是如果我们用 "optimize code" 创建网站应用程序就会出现无限循环,因为 'i' 的值永远不会改变。没有 "optimize code" 这按预期工作。 如何测试: 在空 asp.net 网站应用程序中创建(我尝试 .net 4.5.2 和 .net 4.6)。将此代码添加到全局应用程序 class
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);
}
}
项目属性 -> 构建 -> 检查 "optimize code"。 运行 申请。获取无限循环。 这是正确的行为还是编译器中的错误?
Upd1:
因此,unar 递减和重新抛出异常似乎是非常罕见的情况。
在 windows 7 上的 VS 2015 中编译时重复(在多台机器上尝试)。在 VS2015 中 windows 10 工作正常。
如果像这样更改代码也可以工作
int i = 3;
do
{
try
{
throw new Exception();
}
catch (Exception) when (--i > 0)
{
Thread.Sleep(10);
}
} while (true);
哪个更适合现实世界的例子(因为展开的堆栈)
快速修复。您可以将递减逻辑移到 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);
}
好的。我现在可以说这是一个错误。测试显示您的代码在 32 位模式下工作正常。但不适用于 64 位模式。
我认为这可能是一个错误。 IMO,你应该这样报告。不管是不是,虽然我不建议你采用这种方法。
首先,您在异常过滤器中产生了副作用。通常这可能是一种不好的做法。用 CQS 的术语来思考它;过滤器是查询,而不是命令。
其次,你并没有真正得到任何东西。因为您在下一个块中捕获了相同的异常,所以与仅将逻辑放入第二个 catch 块相比,您从异常过滤器行为(如果不匹配则不展开堆栈)中获得了什么?没有。
代码:
int i = 3;
do
{
try
{
throw new Exception();
}
catch (Exception)
{
if (--i < 0)
throw;
Thread.Sleep(10);
}
} while (true);
表示您总是想捕获异常,但您希望在捕获该异常时根据其他条件采取不同的行为。这使得它比异常过滤器更好地表达了重试概念,异常过滤器表达了您只想捕获异常的想法,具体取决于其他条件。