也许我遗漏了一些明显的东西。考虑以下代码:
string str;
try
{
str = "";
}
catch (Exception)
{
str = "";
}
finally
{
Console.WriteLine(str);
}
Console.WriteLine(str); //this compiles
这里编译器显示了众所周知的错误CS0165:使用未分配的局部变量“str”。我知道我可以通过执行 string str = null
来修复它。但是,str在哪个执行路径中可能没有被初始化呢?
最佳答案
提供另一种与线程无关的方式来看待这个问题:编译器以这种方式运行因为这是指定语言的方式。
来自 ECMA C# 5 规范第 10.4.4.16 节:
Try-catch-finally statements
Definite assignment analysis for a
try-catch-finally
statement of the form:try try-block catch ( … ) catch-block-1 … catch ( … ) catch-block-n finally finally-block
is done as if the statement were a try-finally statement enclosing a try-catch statement:
try { try try-block catch ( … ) catch-block-1 … catch ( … ) catch-block-n } finally finally-block
那么 try-finally 语句在明确赋值方面是如何工作的呢?这在第 10.4.4.16 节中:
For a
try
statement stmt of the form:try try-block finally finally-block
- ...
- The definite assignment state of v at the beginning of finally-block is the same as the definite assignment state of v at the beginning of stmt.
那么这对你来说意味着什么?在语句的开头,您的变量 str
并未明确分配...因此根据这些规则,它也在 finally 的开头也未明确分配
block 。
现在,为什么语言要这样设计?这是一个稍微不同的问题。我不认为这与线程有任何关系。该语言通常假设任何东西都可以引发异常。变量明确分配的唯一方法是为其分配一个值,并且分配完成时不会引发异常。即使在赋值之前发生异常也可能发生的任何代码路径都不能被认为是明确地分配了变量。
作为一个简单的示例,假设我们将您的代码更改为:
string str;
try
{
str = MethodThatThrowsAnException();
}
catch (Exception)
{
str = MethodThatThrowsAnException();
}
finally
{
Console.WriteLine(str);
}
此时,str
未明确分配似乎并不奇怪。只是因为它分配了一个字符串文字,所以看起来不可能失败。但我可以想象,如果这是第一次看到该字符串常量,并且它需要分配一个 String 对象,那么即使分配一个字符串文字也会失败……分配可能会失败。然后还有所有其他可能引发异常的方式,包括中止线程。
所有这些意味着:
try
block 中的第一个语句可以引发异常catch
block 中的第一个语句可以引发异常
在这种情况下 - 无论发生什么情况,也无论它是否与线程有关(例如,可能是分配失败) - 您将不会执行对 str
的任何赋值>,因此它没有定义的值可供读取。
关于C# 局部变量在 Try-Catch-Finally 中访问之前可能不会初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50882359/