什么时候对 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
}
}
假设一个方法正在更改通过引用传递的参数的值。此类操作的效果是在整个应用程序中立即可见还是仅在方法 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
}
}