在 C# 中,“ref”对引用类型变量有什么用?

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

提问人:Andreas Grech 提问时间:6/7/2009 最后编辑:Peter MortensenAndreas Grech 更新时间:3/17/2020 访问量:50002

问:

我知道,如果我将值类型(、等)作为参数(不带关键字)传递,则该变量的副本将传递给该方法,但是如果我使用关键字,则会传递对该变量的引用,而不是新变量。intstructrefref

但是对于引用类型,如类,即使没有关键字,引用也会传递给方法,而不是副本。那么,带有 reference-types 的关键字有什么用呢?refref


举个例子:

var x = new Foo();

以下有什么区别?

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

void Bar(ref Foo y) {
    y.Name = "2";
}
C# 参数 按引用传递

评论


答:

37赞 Mehrdad Afshari 6/7/2009 #1

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

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]);

评论

1赞 Chuck Lu 10/14/2020
这个答案给出了为什么我需要更改原始引用而不是设置上一个实例的属性值的情况。
190赞 user7116 6/7/2009 #2

您可以更改要使用的点:fooy

Foo foo = new Foo("1");

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

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

评论

22赞 lhahne 6/7/2009
所以你基本上得到了一个对原始引用的引用
2赞 user7116 6/7/2009
您可以更改原始引用“引用”的内容,所以是的。
1赞 Andreas Grech 6/8/2009
克里斯,你的解释很棒;感谢您帮助我理解这个概念。
8赞 Tom Hazel 4/24/2012
所以在对象上使用“ref”就像在 C++ 中使用双指针一样?
4赞 user7116 4/24/2012
@TomHazel: -ish,前提是您在C++中使用“双”指针来更改指针指向的内容。
5赞 Hans Van Slooten 6/7/2009 #3

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

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

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

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

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

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}
10赞 Rytmis 6/7/2009 #4

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

C# 也有 'out' 关键字,它与 ref 非常相似,只是使用 'ref' 时,必须在调用方法之前初始化参数,而使用 'out' 时,必须在接收方法中分配一个值。

4赞 Svend 6/7/2009 #5

另一堆代码

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;
    }
}
22赞 Noldorin 6/7/2009 #6

Jon Skeet 写了一篇关于在 C# 中传递参数的好文章。它清楚地详细说明了按值、引用 () 和输出 () 传递参数的确切行为和用法。refout

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

引用参数不传递 中使用的变量的值 函数成员调用 - 他们使用 变量本身。而不是 创建新的存储位置 函数成员中的变量 声明,相同的存储位置 ,所以变量的值 在函数成员和值中 的引用参数将始终 是一样的。参考参数需要 ref 修饰符作为两者的一部分 声明和调用 - 那 意味着当你 通过引用传递某些内容。

评论

11赞 corlettk 6/7/2009
我喜欢把你的狗皮带传给朋友的类比,以传递参考值......不过它很快就坏了,因为我想你可能会注意到,如果你的朋友在把皮带还给你之前把你的卖给了杜宾犬;-)
21赞 himanshupareek66 2/19/2014 #7

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

文章摘要:

引用类型的变量不直接包含其数据;它 包含对其数据的引用。传递引用类型时 参数,可以更改指向的数据 引用,例如类成员的值。但是,您 不能更改引用本身的值;也就是说,你不能 使用相同的引用为新类分配内存并拥有它 在块外保留。为此,请使用 ref 或 out 关键字。

评论

4赞 Marcel 1/6/2015
解释确实很好。但是,在 SO 上不鼓励仅使用链接答案。为了方便读者,我在这里添加了文章中的摘要。
1赞 Petar Drianov 3/7/2014 #8

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

值类型: Indentifier(contains value =堆栈值的地址)---->值类型的值

参考类型: 标识符(包含值=堆栈值的地址)---->(包含值=堆值的地址)---->堆值(通常包含指向其他值的地址),想象一下,更多的箭头在不同的方向上伸向 Array[0]、Array[1]、array[2]

更改值的唯一方法是按照箭头操作。如果一个箭头丢失/更改,则无法访问该值。

3赞 springy76 11/17/2016 #9

除了现有的答案:

正如您询问的 2 种方法的区别: 使用 或 时没有协方差:refout

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
}
-1赞 srbh 7/30/2018 #10

引用变量将地址从一个地方带到另一个地方,因此在任何地方对它们的任何更新都会反映在所有地方,那么 REF 有什么用。 引用变量 (405) 一直有效,直到没有为方法中传递的引用变量分配新内存。

一旦分配了新的内存(410),那么这个对象(408)上的值变化就不会反映在任何地方。 因为这个参考来了。Ref 是引用的引用,因此每当新内存分配它时,它都会知道,因为它指向该位置,因此该值可以由 everyOne 共享。您可以查看图像以获得更清晰的信息。

Ref in Reference Variable