在 C# 中定义的行为是什么,当在设置 out 参数的值之前引发异常,然后您尝试访问该参数时?
public void DoSomething(int id, out control)
{
try
{
int results = Call(Id);
control = new CallControl(results);
}
catch(Exception e)
{
throw new Exception("Call failed", e);
}
}
//somewhere else
DoSomething(out Control control)
{
try
{
DoSomething(1, out control);
}
catch()
{
// handle exception
}
}
// later
Control control;
DoSomething(out control)
control.Show();
编译器通常会抱怨在设置 out 参数之前退出方法。这似乎比保护我自己更聪明。
在设置 out 参数的值之前引发异常,然后尝试访问该参数时,C# 中定义的行为是什么?
你不能这样做。该变量仍然不会被明确分配,除非它在方法调用之前被明确分配。
如果变量在方法调用之前被明确赋值,那么它仍将被明确赋值 - 但除非在抛出异常之前赋值的方法,否则变量的值将保持不变:
class Test
{
static void JustThrow(out int x)
{
throw new Exception();
}
static void Main()
{
int y = 10;
try
{
JustThrow(out y);
}
catch
{
// Ignore
}
Console.WriteLine(y); // Still prints 10
}
}
关系。如果抛出DoSomething
,则不执行control.Show()
。
无论如何,您都无法胜过编译器,因为它知道控制流何时可以绕过初始化。
考虑以下代码,它类似于您提出的代码:
using System;
namespace Demo
{
internal class Program
{
private void run()
{
int a;
try
{
DoSomething(out a);
}
catch {} // Evil empty catch.
Console.WriteLine(a); // Compile error.
}
public void DoSomething(out int x)
{
test();
x = 0;
}
private static void test()
{
throw new InvalidOperationException("X");
}
private static void Main()
{
new Program().run();
}
}
}
它给出了编译错误,因为您在DoSomething()
周围放置了一个 try/catch,因此编译器假定a
可能尚未初始化。
(请注意,如果未显式初始化所有堆栈变量,则会将其初始化为其默认值,但这不会影响编译器的控制流分析。编译器关心的是明确的赋值,而不是 CLR 执行的默认初始化。无论如何,C# 标准不保证此初始化。
如果将空捕获更改为抛出的捕获,则编译错误将消失:
catch { throw new Exception("Test"); } // Compile error goes away.
因为现在如果发生异常,您将无法访问使用 a
的代码。