关于参数:C#中引用类型变量的“ref”有什么用?

What is the use of “ref” for reference-type variables in C#?

我理解,如果我传递一个值类型(intstruct等)作为参数(不带ref关键字),该变量的一个副本将传递给方法,但是如果我使用ref关键字,将传递对该变量的引用,而不是新的引用。

但对于引用类型(如类),即使没有ref关键字,也会将引用传递给方法,而不是一个副本。那么,ref关键字在引用类型中的用途是什么?

例如:

1
var x = new Foo();

以下两者有什么区别?

1
2
3
void Bar(Foo y) {
    y.Name ="2";
}

1
2
3
void Bar(ref Foo y) {
    y.Name ="2";
}

您可以更改foo指向使用y的内容:

1
2
3
4
5
6
7
8
9
Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name =="2"


在某些情况下,您希望修改实际引用而不是指向的对象:

1
2
3
4
5
6
7
8
void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] {"0","1" };
Swap(ref test[0], ref test[1]);


乔恩·斯基特写了一篇关于C中参数传递的伟大文章。它清楚地详细说明了按值、按引用(ref和按输出(out传递参数)的确切行为和用法。

以下是该页面中与ref参数相关的重要引述:

Reference parameters don't pass the
values of the variables used in the
function member invocation - they use
the variables themselves. Rather than
creating a new storage location for
the variable in the function member
declaration, the same storage location
is used, so the value of the variable
in the function member and the value
of the reference parameter will always
be the same. Reference parameters need
the ref modifier as part of both the
declaration and the invocation - that
means it's always clear when you're
passing something by reference.


这里解释得很好:http://msdn.microsoft.com/en-us/library/s6938f28.aspx

文章摘要:

A variable of a reference type does not contain its data directly; it
contains a reference to its data. When you pass a reference-type
parameter by value, it is possible to change the data pointed to by
the reference, such as the value of a class member. However, you
cannot change the value of the reference itself; that is, you cannot
use the same reference to allocate memory for a new class and have it
persist outside the block. To do that, pass the parameter using the
ref or out keyword.


当传递带有ref关键字的引用类型时,将通过引用传递引用,并且调用的方法可以为参数分配新值。该更改将传播到调用范围。如果没有引用,则通过值传递引用,而不会发生这种情况。

C也有"out"关键字,这与ref非常相似,除了使用"ref"时,必须在调用方法之前初始化参数,使用"out"时,必须在接收方法中指定一个值。


它允许您修改传入的引用。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name ="2";

    // Overwrite the reference
    y = new Foo();
}

如果不关心传入的引用,也可以使用out:

1
2
3
4
5
6
7
8
9
10
11
void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}


另一组代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 :" + o1.prop.ToString());
        Console.WriteLine("2 :" + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}


除现有答案外:

正如您所要求的两种方法的不同之处:使用refout时没有co(ntra)差异:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}

一个方法中的参数似乎总是传递一个副本,问题是什么的副本。复制是由对象的复制构造函数完成的,因为所有变量都是C中的对象,所以我认为这是所有变量的情况。变量(对象)就像生活在某个地址的人。我们或者改变居住在这些地址的人,或者我们可以在电话簿中创建更多对居住在这些地址的人的引用(制作浅薄的副本)。因此,多个标识符可以引用同一个地址。引用类型需要更多的空间,因此与直接通过箭头连接到堆栈中标识符的值类型不同,它们在堆中具有另一个地址的值(要驻留的空间更大)。此空间需要从堆中获取。

值类型:标识符(包含值=堆栈值的地址)--->值类型的值

参考类型:标识符(包含值=堆栈值的地址)--->(包含值=堆值的地址)--->堆值(最常包含其他值的地址),想象更多箭头沿不同方向指向数组[0]、数组[1]、数组[2]

更改值的唯一方法是跟随箭头。如果一个箭头以无法访问的方式丢失/更改。


引用变量将地址从一个地方传递到另一个地方,因此在任何地方对它们的任何更新都将反映到所有地方,那么引用的用途是什么。在没有新内存分配给方法中传递的引用变量之前,引用变量(405)是好的。

一旦分配了新的内存(410),那么这个对象(408)上的值更改就不会反映到任何地方。因为这个裁判来了。引用是引用的引用,因此每当新内存分配时,它就会知道,因为它指向该位置,因此每个人都可以共享该值。你可以看到更清晰的图像。

Ref in Reference Variable