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

但是对于引用类型,就像类一样,即使没有ref关键字,也会将引用传递给方法,而不是副本。那么ref关键字与reference-types有什么用呢?


举个例子:

var x = new Foo();

以下两种有什么区别?

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

and

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

当前回答

除了现有的答案:

正如你所要求的2种方法的差异:使用ref或out时没有co(ntra)方差:

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
}

其他回答

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

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

Jon Skeet写了一篇关于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.

A parameter in a method seems to be always passing a copy, the question is a copy of what. A copy is done by a copy constructor for an object and since all variables are Object in C#, i believe this is the case for all of them. Variables(objects) are like people living at some addresses. We either change the people living at those addresses or we can create more references to the people living at those addresses in the phone book(make shallow copies). So, more than one identifier can refer to the same address. Reference types desire more space, so unlike value types that are directly connected by an arrow to their identifier in the stack, they have value for another address in the heap( a bigger space to dwell). This space needs to be taken from the heap.

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

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

更改值的唯一方法是遵循箭头。如果一个箭头丢失/改变了该值不可达的方式。

除了现有的答案:

正如你所要求的2种方法的差异:使用ref或out时没有co(ntra)方差:

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
}

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

一旦分配了新的内存(410),那么这个对象(408)上的值变化将不会反映在所有地方。 裁判来了。Ref是引用的引用,所以每当分配新的内存时,它就会知道,因为它指向那个位置,因此这个值可以被每个人共享。您可以更清楚地看到图像。