什么时候对 ref 参数的赋值在 C# 中有效?

When is assignment to a ref argument effective in C#?

假设一个方法正在更改通过引用传递的参数的值。此类操作的效果是在整个应用程序中立即可见还是仅在方法 returns?

之后可见

下面是一个重要的例子:

int x = 0;
void Foo(ref int y)
{
    ++y;
    Console.WriteLine(x);
}
Foo(ref x);

在C# Pad下可以是运行http://csharppad.com/gist/915318e2cc0da2c2533dfa7983119869

函数 Foo 可以访问变量 x 因为它在同一范围内,而且恰好在调用站点接收到对它的引用。如果 ++y 的效果是立竿见影的,输出应该是 1,但我可以想象一个编译器生成代码,例如,将本地值存储在寄存器中并在稍后转储到内存return 之前的时间。语言规范是否确保输出为 1 或是否允许抖动优化,使输出实现依赖?

C# 规范保证在单线程上下文中 运行 时必须观察到所有操作按顺序发生。因此,对于您提供的输出 0 的程序来说,这将是一个无效的优化,因为这会导致从单个线程观察到重新排序。

当您提供 ref 参数时,关键在于 该参数是引用变量 的别名。它不是副本;只有在方法完成后才能观察到更改。相反,在您的程序 中使用 y 在语义上与使用 x 相同,因为两个标识符引用相同的存储位置。

我会注意到您的程序只会在使用 ref 参数 returns 的方法之后访问变量,因此它实际上并没有回答您的问题。根据 ref 参数是否实际上是对同一变量的引用,或者如果它只是在方法结束时将值复制回来,一个程序片段实际上会像这样:

public static void Foo(ref int y, Func<int> function)
{
    y = 42;
    Console.WriteLine(function());
}

int x = 7;
Foo(ref x, () => x);

ref 是存储位置的 别名 ref 参数指向您传入的完全相同的变量,所以是的,赋值立即可见。

Is the effect of such operation immediately visible across the application or only after the method returns?

立即可见 - 因为基本上,您最终传递的是变量本身,而不是变量的值。您正在修改完全相同的存储位置。

确实,您可以在相同的方法中看到这一点:

using System;

class Test
{
    static void Main(string[] args)
    {
        int a = 10;
        Foo(ref a, ref a);
    }

    static void Foo(ref int x, ref int y)
    {
        x = 2;
        Console.WriteLine(y); // Prints 2, because x and y share a storage location
    }
}

这是 C# 5 规范中的第 5.1.5 节:

A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member or anonymous function invocation. Thus, the value of a reference parameter is always the same as the underlying variable.

反之亦然,顺便说一句 - 如果底层变量的值以其他方式更改,则该更改将在方法中可见。使用委托更改值的示例:

using System;

class Test
{
    static void Main(string[] args)
    {
        int a = 10;
        Foo(ref a, () => a++);
    }

    static void Foo(ref int x, Action action)
    {
        Console.WriteLine(x); // 10
        action();             // Changes the value of a
        Console.WriteLine(x); // 11
        x = 5;
        action();
        Console.WriteLine(x); // 6
    }
}