39

假设我有以下代码:

    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 规范,但找不到自己)

4

5 回答 5

31

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

于 2012-01-18T07:57:27.027 回答
8

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

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

来自MSDN

于 2012-01-18T07:55:21.463 回答
5

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

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

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

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
}
于 2012-01-18T08:10:29.860 回答
3

它是从 的角度保证的,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
}

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

于 2012-01-18T09:24:29.503 回答
0

来自C# 语言规范部分 5.1.6 输出参数

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

换句话说

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

在实践中

  • 你永远不需要担心这个。
  • 如果您尝试使用未分配的变量,编译器将生成错误。
  • 所以C# 中的 out 参数是完全安全的——如果它们编译,它们就可以工作。
于 2018-03-13T14:32:45.960 回答