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

时间:2009-06-07 11:11:26

标签: c# parameters pass-by-reference pass-by-value

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

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


以例如:

var x = new Foo();

以下是什么区别?

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

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

10 个答案:

答案 0 :(得分:144)

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

Foo foo = new Foo("1");

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

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

答案 1 :(得分:29)

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

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

答案 2 :(得分:17)

Jon Skeet写了关于C#参数传递的a great article。它详细说明了按值,引用(ref)和输出(out)传递参数的确切行为和用法。

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

  

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

答案 3 :(得分:15)

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

文章摘要:

  

引用类型的变量不直接包含其数据;它   包含对其数据的引用。传递引用类型时   参数值,可以更改指向的数据   引用,例如类成员的值。但是,你   不能改变引用本身的值;也就是说,你做不到   使用相同的引用为新类分配内存并拥有它   在街区外坚持。为此,请使用以下命令传递参数   引用或退出关键字。

答案 4 :(得分:9)

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

C#也有'out'关键字,它与ref非常相似,除了使用'ref'之外,必须在调用方法之前初始化参数,并且使用'out'必须在接收方法中指定一个值。 / p>

答案 5 :(得分: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();
}

答案 6 :(得分: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;
    }
}

答案 7 :(得分:3)

除现有答案外:

当您询问两种方法的区别时:使用outclass 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 } 时没有co(ntra)方差:

<?php
$string = "0 3 28 36 42 66 73 80 103 125 997 1003 1027 2006 3001";
$data= explode( ' ', $string );

$multiple = 1;
foreach( $data as $value )
{
  if( (int)$value > ( 1000 * $multiple ) ) 
  {
    // Passed a 1000 marker
    echo $value."\n";
    $multiple++;
  }
}

答案 8 :(得分:1)

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

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

参考类型: 标识符(包含值=堆栈值的地址)----&gt;(包含值=堆值的地址)----&gt;堆值(通常包含其他值的地址),想象更多箭头粘贴在不同的方向到Array [0],Array [1],array [2]

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

答案 9 :(得分:-1)

引用变量将地址从一个地方传送到另一个地方,因此在任何地方对其进行的任何更新都会在所有地方反映出来,那么REF的用途是什么。 直到没有新的内存分配给在方法中传递的参考变量之前,参考变量(405)才是好的。

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

Ref in Reference Variable