【问题标题】:Out parameters and exceptions输出参数和异常
【发布时间】:2012-02-12 23:28:22
【问题描述】:

假设我有以下代码:

    static void Fjuk(out string str)
    {
        str = "fjuk!";
        throw new Exception();
    }

    static void Main(string[] args)
    {
        string s = null;
        try
        {
            Fjuk(out s);
        }
        catch (Exception)
        {
            Console.WriteLine(s ?? "");
        }
    }

当我测试它时,s 已被初始化为“fjuk!”在catch 块中使用时。
这是由规范保证还是依赖于实现? (我搜索了 C# 3 规范,但找不到自己)

【问题讨论】:

  • 我不知道规范,但它肯定是我所期望的。我希望成员变量、属性等的初始化在您的 catch 块中仍然可用。
  • 当你需要他时,埃里克·利珀特在哪里...... :)
  • @jb。 MSDN 怎么了?
  • @gdoron 既然可以向老师本人学习,为什么还要阅读二手教科书?

标签: c# exception out


【解决方案1】:

差不多,这是out 含义的一个方面;首先,请注意out 并不真正存在——我们只需要考虑refout 只是ref,在编译器中进行了一些“明确的分配”调整)。 ref 的意思是“传递 this 的地址”——如果我们通过地址改变值,则显示 立即——毕竟,它是更新 @ 堆栈上的内存 987654327@。它不能抽象这个(延迟写入),因为该值可能是,例如,一些使用ref 的超大结构,专门用于避免在堆栈上复制它(一种在 XNA 等中广泛使用的方法) .

【讨论】:

    【解决方案2】:

    这是“保证”,因为out参数会随着参数的memory address改变值。

    out 关键字导致参数通过引用传递。这类似于 ref 关键字,只是 ref 要求在传递之前对变量进行初始化。

    来自MSDN

    【讨论】:

      【解决方案3】:

      如果方法抛出异常,则不保证设置输出参数。如果方法无异常退出,则保证设置了输出参数。

      在您的情况下,该方法将始终设置输出参数,但编译器不会以这种方式分析该方法的代码。如果方法异常退出,输出参数仍然不被认为是确定设置的。

      您在异常处理程序中的代码不依赖于方法调用设置的变量,因为您是在创建变量时设置变量。如果在创建变量的时候不设置,异常处理程序就无法使用,因为不保证设置:

      string s;
      try {
        Fjuk(out s);
        Console.WriteLine(s); // here the variable is guaranteed to be set
      } catch (Exception) {
        Console.WriteLine(s); // here it's not, so this won't compile
      }
      

      【讨论】:

      • 是的,我认为很明显它不会检查被调用方法中的代码,但在我的示例中并没有说明这一点。感谢您指出这一点!
      【解决方案4】:

      Fjuk 的角度保证,但从Main 的角度保证。

      Fjuk中设置参数后抛出异常。虽然编译器、抖动和 CPU 可以进行重新排序,但不会有重新排序使得单个线程观察到的顺序发生变化。如果在抛出异常之前没有设置参数,单个线程可以“注意到”,因此保证设置了参数。

      虽然在Main 中,我们不知道Fjuk 的实现细节,所以编译器在分析Main 时不能依赖它。因此,在我们没有在调用之前为s 赋值的变体中:

      static void Main()
      {
          string s;
          try
          {
              Fjuk(out s);
              Console.WriteLine(s ?? "");//fine
          }
          catch (Exception)
          {
              Console.WriteLine(s ?? "");//compiler error
          }
          Console.WriteLine(s ?? "");//compiler error
      }
      

      在调用Fjuk 后立即使用s 的第一次尝试很好,因为只有Fjuk 成功才能到达那里,如果Fjuk 成功则必须分配s。但是在第二种和第三种情况下,有可能在没有Fjuk 成功的情况下到达这些行,并且由于无法通过对Main 的分析来判断是否可以在设置s 之前引发异常,所以@987654337 的使用@ 必须被禁止。

      【讨论】:

        【解决方案5】:

        来自C# Language Specification 部分 5.1.6 输出参数

        在函数成员或委托调用正常完成后,作为输出参数传递的每个变量都被视为在该执行路径中分配。

        换句话说

        • 始终分配out 参数。
        • 任何以前的值都会被覆盖。
        • 但如果函数抛出异常,编译器会假定它们没有被赋值。

        在实践中

        • 您永远不必担心这一点。
        • 如果您尝试使用未分配的变量,编译器将生成错误。
        • 所以C# 中的输出参数是完全安全的 - 如果它们编译,它们就可以工作。

        【讨论】:

          猜你喜欢
          • 2016-09-12
          • 2011-12-07
          • 2019-06-20
          • 1970-01-01
          • 2012-08-26
          • 1970-01-01
          • 2013-11-18
          • 1970-01-01
          相关资源
          最近更新 更多