i
在这个例子中什么时候可以取消分配?
int i;
try
{
i = 2;
}
catch
{
i = 3;
}
finally
{
string a = i.ToString();
}
i
在这个例子中什么时候可以取消分配?
int i;
try
{
i = 2;
}
catch
{
i = 3;
}
finally
{
string a = i.ToString();
}
You could get a ThreadAbortException before i=2 runs, for example. Anyway, the C# compiler is not exceptionally smart, so it's easy to fool with contrived examples like the above one. It doesn't have to recognize every situation, and if it's not sure it is assigned, even if you are sure, it will complain.
EDIT: I was a bit quick on my first assumption. So to refine it, here's what I think. Code is guaranteed to run in order, or if an exception happens, it will jump to the handlers. So i=2 may not run if an exception happens before that. I still claim that a ThreadAbortException is one of the few reasons why this can happen, even you have no code which could produce exceptions. Generally, if you have any number of different exception handlers, the compiler cannot know in advance which one will run. So it doesn't try to make any assumptions about that. It could know that if 1) there is only 1 catch block and 2) it is typeless, then, and only then, that one catch block is guaranteed to run. Or, if there were multiple catch handlers, and you assigned your variable in every one of them, it could also work, but I guess the compiler doesn't care about that either. However simple it may seem, it is a special case, and the C# compiler team has a tendency to ignore those special cases.
您发布的示例不太可能发生这种情况。但是,在这种情况下,编译器将“有帮助”。正如 Hadas 所说,只需将 i 初始化为 0。
可以编写许多代码示例,其中可以证明变量已分配但编译器根本无法证明它确实已分配。
只需考虑这个更简单的情况:
int i;
if ((bool)(object)true)
i = 0;
Console.WriteLine(i);
可以证明,这种情况下也无法访问未分配i
的对象,但它不会编译。
在一般情况下,编译器也被证明不可能解决这个问题。在某些情况下,它可以证明一个变量肯定不是绝对赋值的,在某些情况下它可以证明它肯定是赋值的,但也有它不知道任何一种方式的情况。在这些情况下,它选择失败,因为它认为一些误报错误比误报的危害更小。
更多地谈论您的具体案例;您是说如果在try
和catch
块中都分配了一个变量,那么它肯定是分配的。尽管您的特定代码可能是这样,但在一般情况下肯定不是这样。您需要考虑catch
块未处理的异常(即使在您的情况下,未指定任何异常,也不会捕获堆栈溢出或内存不足等异常),您需要考虑 catch 块本身抛出一个异常(同样,在您的情况下不会发生,但编译器需要证明这一点才能编译代码)。
我认为如果您查看原始类型之外的另一种类型会有所帮助。考虑:
int i;
MyClass ed = new MyClass();
try
{
int newI = ed.getIntFromFunctionThatWillThrow();
i = newI;
}
catch (Exception e)
{
i = 3;
// do some abortion code.
}
finally
{
string a = i.ToString();
...
}
所以在这段代码中,任何一个执行分支都没有字典上的保证。你有两个(至少)分支需要考虑:try-finally 分支和 try-catch 分支。由于函数“getIntFromFunctionThatWillThrow”将被抛出(看看我在那里做了什么?)即使我稍后尝试使用它,我也将被保留。但是,直到运行时之后才能识别出来,就像我不知道如果我没有关于 ed 上的成员的内部信息,这将进入哪一段代码。所以编译器不知道我将是什么值。它只知道它存在,并且是 int 类型。
如果这是一个问题,解决方法是设置 i 一个初始值,这将抑制错误。
我希望这会有所帮助!