在c#中“ref”用于引用类型变量的使用是什么?

时间:2021-12-05 16:41:04

I understand that if I pass a value-type (int, struct, etc.) as a parameter (without the ref keyword), a copy of that variable is passed to the method, but if I use the ref keyword a reference to that variable is passed, not a new one.

我理解,如果我将值类型(int、struct等)作为参数(没有ref关键字)传递给方法,那么该变量的副本将被传递给方法,但是如果我使用ref关键字传递该变量的引用,而不是新的引用。

But with reference-types, like classes, even without the ref keyword, a reference is passed to the method, not a copy. So what is the use of the ref keyword with reference-types?

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


Take for example:

举个例子:

var x = new Foo();

What is the difference between the following?

下面的区别是什么?

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

and

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

9 个解决方案

#1


129  

You can change what foo points to using y:

你可以改变foo指向什么用y:

Foo foo = new Foo("1");

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

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

#2


27  

There are cases where you want to modify the actual reference and not the object pointed to:

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

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

#3


15  

Jon Skeet wrote a great article about parameter passing in C#. It details clearly the exact behaviour and usage of passing parameters by value, by reference (ref), and by output (out).

Jon Skeet写了一篇关于c#的参数传递的文章。它清楚地描述了按值、引用(ref)和输出(out)传递参数的确切行为和用法。

Here's an important quote from that page in relation to ref parameters:

这是一页关于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.

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

#4


13  

Very nicely explained here : http://msdn.microsoft.com/en-us/library/s6938f28.aspx

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

Abstract from the article:

摘要从文章:

A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword.

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

#5


9  

When you pass a reference type with the ref keyword, you pass the reference by reference, and the method you call can assign a new value to the parameter. That change will propagate to the calling scope. Without ref, the reference is passed by value, and this doesn't happen.

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

C# also has the 'out' keyword which is a lot like ref, except that with 'ref', arguments must be initialized before calling the method, and with 'out' you must assign a value in the receiving method.

c#也有'out'关键字,这很像ref,除了'ref'之外,在调用方法之前必须初始化参数,而'out'则必须在接收方法中分配值。

#6


5  

It allows you to modify the reference passed in. 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();
}

You can also use out if you don't care about the reference passed in:

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

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

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

#7


4  

Another bunch of code

另一堆代码

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

#8


3  

In addition to the existing answers:

除了现有的答案之外:

As you asked for the difference of the 2 methods: There is no co(ntra)variance when using ref or out:

如您询问两种方法的差异:使用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
}

#9


1  

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.

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

Value type: Indentifier(contains value =address of stack value)---->Value of value type

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

Reference type: Identifier(contains value=address of stack value)---->(contains value=address of heap value)---->Heap value(most often contains addresses to other values), imagine more arrows sticking in different directions to Array[0], Array[1], array[2]

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

The only way to change a value is to follow the arrows. If one arrow gets lost/changed in the way the value is unreachable.

改变一个值的唯一方法是跟随箭头。如果一个箭头以不可达的方式丢失/更改,则该值不可达。

#1


129  

You can change what foo points to using y:

你可以改变foo指向什么用y:

Foo foo = new Foo("1");

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

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

#2


27  

There are cases where you want to modify the actual reference and not the object pointed to:

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

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

#3


15  

Jon Skeet wrote a great article about parameter passing in C#. It details clearly the exact behaviour and usage of passing parameters by value, by reference (ref), and by output (out).

Jon Skeet写了一篇关于c#的参数传递的文章。它清楚地描述了按值、引用(ref)和输出(out)传递参数的确切行为和用法。

Here's an important quote from that page in relation to ref parameters:

这是一页关于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.

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

#4


13  

Very nicely explained here : http://msdn.microsoft.com/en-us/library/s6938f28.aspx

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

Abstract from the article:

摘要从文章:

A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword.

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

#5


9  

When you pass a reference type with the ref keyword, you pass the reference by reference, and the method you call can assign a new value to the parameter. That change will propagate to the calling scope. Without ref, the reference is passed by value, and this doesn't happen.

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

C# also has the 'out' keyword which is a lot like ref, except that with 'ref', arguments must be initialized before calling the method, and with 'out' you must assign a value in the receiving method.

c#也有'out'关键字,这很像ref,除了'ref'之外,在调用方法之前必须初始化参数,而'out'则必须在接收方法中分配值。

#6


5  

It allows you to modify the reference passed in. 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();
}

You can also use out if you don't care about the reference passed in:

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

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

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

#7


4  

Another bunch of code

另一堆代码

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

#8


3  

In addition to the existing answers:

除了现有的答案之外:

As you asked for the difference of the 2 methods: There is no co(ntra)variance when using ref or out:

如您询问两种方法的差异:使用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
}

#9


1  

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.

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

Value type: Indentifier(contains value =address of stack value)---->Value of value type

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

Reference type: Identifier(contains value=address of stack value)---->(contains value=address of heap value)---->Heap value(most often contains addresses to other values), imagine more arrows sticking in different directions to Array[0], Array[1], array[2]

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

The only way to change a value is to follow the arrows. If one arrow gets lost/changed in the way the value is unreachable.

改变一个值的唯一方法是跟随箭头。如果一个箭头以不可达的方式丢失/更改,则该值不可达。