“有什么用?”;参考「;对于C#中的引用类型变量?

“有什么用?”;参考「;对于C#中的引用类型变量?,c#,parameters,pass-by-reference,pass-by-value,C#,Parameters,Pass By Reference,Pass By Value,我知道,如果我将值类型(int,struct等)作为参数传递(没有ref关键字),该变量的副本将传递给方法,但如果我使用ref关键字,则传递对该变量的引用,而不是新的引用 但是对于引用类型,比如类,即使没有ref关键字,也会将引用传递给方法,而不是副本。那么ref关键字与引用类型有什么关系呢 例如: var x = new Foo(); 以下两者之间的区别是什么 void Bar(Foo y) { y.Name = "2"; } 及 在某些情况下,您需要修改实际的参考,而不是指向的

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

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


例如:

var x = new Foo();
以下两者之间的区别是什么

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


在某些情况下,您需要修改实际的参考,而不是指向的对象:

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]);
void Swap(参考T x,参考T y){
T=x;
x=y;
y=t;
}
var test=new[]{“0”,“1”};
交换(参考测试[0],参考测试[1]);

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

Foo foo = new Foo("1");

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

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

它允许您修改传入的引用。e、 g

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();
}

当您传递带有ref关键字的引用类型时,您将逐个引用传递引用,并且您调用的方法可以为参数指定一个新值。该更改将传播到调用范围。如果没有ref,引用将按值传递,而这不会发生

C#也有'out'关键字,它与ref非常相似,除了'ref'的参数必须在调用方法之前初始化,而'out'的参数必须在接收方法中赋值。

另一组代码

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;
    }
}
Jon Skeet写了一篇关于C#中的参数传递的文章。它清楚地详细说明了按值、按引用(
ref
)和按输出(
out
)传递参数的确切行为和用法

以下是该页中有关
ref
参数的重要引用:

引用参数不通过 中使用的变量的值 函数成员调用-它们使用 变量本身。而不是 为创建新的存储位置 函数成员中的变量 声明,相同的存储位置 已使用,因此变量的值 在函数成员和值中 引用参数的 都一样。参考参数需要 ref修饰符作为 声明和调用-即 这意味着当你 通过引用传递某物


这里有很好的解释:

文章摘要:

引用类型的变量不直接包含其数据;信息技术 包含对其数据的引用。当传递引用类型时 参数的值,可以更改 引用,例如类成员的值。然而,你 无法更改引用本身的值;也就是说,你不能 使用相同的引用为一个新类分配内存,并将其保存 坚持在街区之外。为此,请使用 ref或out关键字


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

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

参考类型: 标识符(包含值=堆栈值的地址)--->(包含值=堆值的地址)--->堆值(通常包含指向其他值的地址),想象更多指向数组[0]、数组[1]、数组[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是reference的reference,因此每当分配新内存时,它都会知道,因为它指向该位置,因此每个人都可以共享该值。你可以看到更清晰的图像


所以你基本上得到了对原始参考的引用。你可以改变原始参考“引用”的内容,所以是的。克里斯,你的解释很好;谢谢你帮助我理解这个概念。所以在一个对象上使用“REF”就像使用C++中的双指针?@ TimHaZel:-ISH,如果你用C++中的“双”指针来改变指针指向什么。我喜欢把你的狗皮带传给朋友,以通过值传递引用。但它很快就崩溃了,因为我想你可能会注意到,如果你的朋友在把皮带还给你之前把你的狗屎换成了杜宾;-)这个解释确实很好。然而,不鼓励只提供链接的答案。为了方便阅读,我添加了文章的摘要
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
}