【问题标题】:Use of unassigned local variable on finally block在 finally 块上使用未分配的局部变量
【发布时间】:2013-12-11 14:39:19
【问题描述】:

本例中的i 何时可以取消分配?

int i;
try
{
    i = 2;
}
catch
{
    i = 3;
}
finally
{
    string a = i.ToString();
}

【问题讨论】:

  • 输入 int i = 0;一开始
  • 我知道如何避免错误,我想知道为什么编译器认为这可能是未分配的
  • 您的代码中很少有没有单一内存分配的情况。对于看似相同的问题,除了object i;,如果内存不足,i = 2;i = 3; 都可能会抛出,此时finally 处理程序在初始化之前确实会看到i

标签: c# try-catch local-variables unassigned-variable


【解决方案1】:

例如,您可能会在 i=2 运行之前获得 ThreadAbortException。 无论如何,C# 编译器并不是特别聪明,因此很容易被上面这样的人为示例所欺骗。它不必识别每一种情况,如果它不确定分配给它,即使你确定它也会抱怨。

编辑:我的第一个假设有点快。所以要完善它,这就是我的想法。代码保证按顺序运行,或者如果发生异常,它将跳转到处理程序。因此,如果在此之前发生异常, i=2 可能不会运行。我仍然声称 ThreadAbortException 是发生这种情况的少数原因之一,即使您没有可能产生异常的代码。通常,如果您有任意数量的不同异常处理程序,编译器无法提前知道将运行哪一个。因此,它不会尝试对此做出任何假设。 可以知道,如果 1) 只有 1 个 catch 块并且 2) 它是无类型的,那么,只有这样,一个 catch 块才能保证运行。或者,如果有多个 catch 处理程序,并且您在每个处理程序中分配了变量,它也可以工作,但我猜编译器也不关心这一点。无论看起来多么简单,它都是一种特殊情况,C# 编译器团队倾向于忽略这些特殊情况。

【讨论】:

  • 如果ThreadAbortException 被抛出,它的处理程序仍然运行,所以问题中的catch 块仍然会设置i。我认为你的下一点是正确的答案。
【解决方案2】:

您发布的示例不太可能发生这种情况。但是,在这种情况下,编译器将“有帮助”。正如 Hadas 所说,只需将 i 初始化为 0。

【讨论】:

    【解决方案3】:

    可以编写许多代码示例,其中可以证明变量已分配但编译器根本无法证明它确实已分配。

    只要考虑这个更简单的例子:

    int i;
    if ((bool)(object)true)
        i = 0;
    
    Console.WriteLine(i);
    

    事实证明,这种情况永远也无法访问未分配的i,但它不会编译。

    在一般情况下编译器也可以证明不可能解决这个问题。在某些情况下,它可以证明一个变量肯定不是绝对赋值的,在某些情况下它可以证明它确实是 is 赋值的,但也有它不知道任何一种方式的情况。在这些情况下,它会选择失败,因为它认为一些误报错误比误报的危害更小。

    更多地谈论您的具体案例;你是说如果在trycatch 块中都分配了一个变量,那么它肯定是分配的。尽管您的特定代码可能是这样,但在一般情况下肯定不是这样。您需要考虑 catch 块未处理的异常(即使在您的情况下,未指定任何异常,也不会捕获堆栈溢出或内存不足等异常),您需要考虑 catch阻止自身抛出异常(同样,在您的情况下不会发生,但编译器需要证明这一点才能编译代码)。

    【讨论】:

      【解决方案4】:

      我认为如果您查看原始类型之外的另一种类型会有所帮助。考虑:

      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 一个初始值,这将抑制错误。

      我希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-18
        • 2015-11-09
        • 2012-05-18
        • 1970-01-01
        • 2023-03-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多