200

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

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


举个例子:

var x = new Foo();

以下有什么区别?

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

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

10 回答 10

177

您可以更改foo要使用的点y

Foo foo = new Foo("1");

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

Bar(ref foo);
// foo.Name == "2"
于 2009-06-07T11:13:28.473 回答
34

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

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]);
于 2009-06-07T11:13:20.173 回答
22

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

ref这是该页面中有关参数的重要引用:

引用参数不传递函数成员调用中使用的变量的值——它们使用变量本身。而不是在函数成员声明中为变量创建新的存储位置,而是使用相同的存储位置,因此函数成员中变量的值和引用参数的值将始终相同。引用参数需要 ref 修饰符作为声明和调用的一部分 - 这意味着当您通过引用传递某些内容时它总是很清楚。

于 2009-06-07T11:25:31.617 回答
20

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

文章摘要:

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

于 2014-02-19T14:04:12.037 回答
10

当你使用 ref 关键字传递一个引用类型时,你通过引用传递引用,你调用的方法可以为参数分配一个新值。该更改将传播到调用范围。没有 ref,引用是按值传递的,这不会发生。

C#也有'out'关键字,它很像ref,除了'ref',参数必须在调用方法之前初始化,而'out'你必须在接收方法中赋值。

于 2009-06-07T11:17:31.273 回答
5

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

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();
}
于 2009-06-07T11:15:54.087 回答
4

又一堆代码

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;
    }
}
于 2009-06-07T11:18:27.997 回答
3

除了现有的答案:

正如您询问两种方法的区别:使用refor时没有协(ntra)方差out

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
}
于 2016-11-17T14:41:51.937 回答
1

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

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

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

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

于 2014-03-07T10:10:14.897 回答
-1

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

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

参考变量中的参考

于 2018-07-29T17:52:05.967 回答