在 C# 类中使用 ref

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/971170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:31:53  来源:igfitidea点击:

using ref with class C#

c#pointersreferencekeyword

提问by Nefzen

I want to give a certain linked list to a class I am making. I want the class to write into that list (eg by .addLast()).

我想为我正在制作的课程提供某个链接列表。我希望类写入该列表(例如通过 .addLast())。

Should I use the refkeyword for that?

我应该ref为此使用关键字吗?

I am somewhat puzzled on where to use the refand outkeywords in C#, as all classesare allocated dynamically on the heap and we actually use pointers for most operations.
Of course, outand refkeywords make sense for primitives and structs.

我对在 C# 中在哪里使用refandout关键字感到有些困惑,因为所有都是在堆上动态分配的,我们实际上在大多数操作中使用指针。
当然,outref关键字作出原语和结构感。

Also, if I don't send the list directly, but send a class containing the list? (it's internaland needed), do I still need to use ref? or if I pass it between functions, ex:

另外,如果我不直接发送列表,而是发送包含列表的类?(它是internal需要的),我还需要使用ref吗?或者如果我在函数之间传递它,例如:

void A(ref LinkedList<int> list){
    B(list);
}

void B(ref LinkedList<int> list){
    _myList = list;
}

采纳答案by Jon B

For what you're doing you don't need to use ref. If you did pass the list using ref, you would be allowing the caller to change which list you're referencing, rather than just changing the contents of the list.

对于您正在做的事情,您不需要使用 ref。如果您确实使用 ref 传递了列表,那么您将允许调用者更改您引用的列表,而不仅仅是更改列表的内容。

回答by dss539

No you don't need to use ref.

不,您不需要使用 ref。

LinkedList is an object, so it is already a reference type. The parameter listis a reference to the LinkedList object.

LinkedList 是一个对象,所以它已经是一个引用类型。该参数list是对 LinkedList 对象的引用。

See this MSDN articlefor a description of value types. Value types are usually the parameters you would use the refor outkeywords with.

有关值类型的说明,请参阅此MSDN 文章。值类型通常是您将使用reforout关键字的参数。

You may also want to pass reference types by ref. This will allow you to point the reference to another object.

您可能还想通过ref. 这将允许您将引用指向另一个对象。

Any time you pass an object oyou are really passing a reference to the object. When you pass a `ref object o' you are passing a reference to the reference. This allows to you modify the reference.

任何时候传递 an 时,object o您实际上都在传递对对象的引用。当你传递一个 `ref object o' 时,你传递了一个对引用的引用。这允许您修改参考。

Passing Reference-Type Parametersmay also help you understand.

传递引用类型参数也可以帮助您理解。

回答by Noldorin

This is a common misconception of the use of refkeyword in C#. Its purpose is to pass either a value or a reference type by reference, and you only need it in specific circumstances where you need a direct reference to the actual argument, rather than a copy of the argument (be it a value or reference itself). It is imperative not to confuse reference typeswith passing by referencein any case.

这是对refC#中关键字使用的常见误解。它的目的是通过引用传递值或引用类型,并且只有在需要直接引用实际参数的特定情况下才需要它,而不是参数的副本(无论是值还是引用本身) . 在任何情况下都不要将引用类型按引用传递混淆。

Jon Skeet has written an excellent articleabout parameter passing in C#, which compares and contrasts value types, reference types, passing by value, passing by reference (ref), and output parameters (out). I recommend you take some time to read through this in full and your understanding should become much clearer.

Jon Skeet 写了一篇关于 C# 中参数传递的优秀文章,对值类型、引用类型、按值传递、按引用传递 ( ref) 和输出参数 ( out)进行了比较和对比。我建议您花一些时间完整阅读本文,您的理解应该会变得更加清晰。

To quote the most important parts from that page:

引用该页面中最重要的部分:

Value parameters:

值参数:

By default, parameters are value parameters. This means that a new storage location is created for the variable in the function member declaration, and it starts off with the value that you specify in the function member invocation. If you change that value, that doesn't alter any variables involved in the invocation

默认情况下,参数是值参数。这意味着为函数成员声明中的变量创建了一个新的存储位置,它以您在函数成员调用中指定的值开始。如果您更改该值,则不会更改调用中涉及的任何变量

Reference parameters:

参考参数:

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. Let's look at our previous examples, just changing the parameter to be a reference parameter:

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

To conclude: having read my reply and Jon Skeet's article, I hope that you will then see that there is no need whatsoeverfor using the refkeyword in the context of your question.

总结:在阅读了我的回复和 Jon Skeet 的文章后,我希望您随后会看到没有必要ref在您的问题的上下文中使用关键字。

回答by Timothy Carter

In the two snippets you posted there is no need to pass list by ref. To quote Jon Skeet, object references are passed by value. This means, you would want to ref a reference type when the method will or might change the object reference and you want this new reference to carry back to the calling method. For example:

在您发布的两个片段中,无需通过 ref 传递列表。引用 Jon Skeet 的话,对象引用是按值传递的。这意味着,当方法将要或可能会更改对象引用时,您可能希望引用引用类型,并且您希望此新引用返回到调用方法。例如:

void methodA(string test)
{
    test = "Hello World";
}

void methodB(ref string test)
{
    test = "Hello World";
}

void Runner()
{
    string first= "string";
    methodA(first);
    string second= "string";
    methodB(ref second);
    Console.WriteLine((first == second).ToString()); //this would print false
}

回答by Spencer Ruport

The only time you need to use ref with a reference type is if you're going to be creating a new object inside a function.

唯一需要将 ref 与引用类型一起使用的情况是要在函数内创建新对象。

Example #1: refkeyword not necessary.

示例#1ref不需要关键字。

// ...
   List myList = new List();
   PopulateList(myList);
// ...
void PopulateList(List AList)
{
   AList.Add("Hello");
   AList.Add("World");
}

Example #2: refkeyword necessary.

示例#2ref必需的关键字。

// ...
   List myList;
   PopulateList(ref myList);
// ...
void PopulateList(ref List AList)
{
   AList = new List();
   AList.Add("Hello");
   AList.Add("World");
}

回答by Nefzen

I am adding this answer for programmers that are used to C++ like myself.

我正在为像我这样习惯于 C++ 的程序员添加这个答案。

Classes, interfaces, delegatess and arrays are reference types, meaning that they have an underlying pointer. Normal function calls copy this pointer(reference) by value, while sending by reference sends sends a reference to this reference:

类、接口、委托和数组都是reference types,这意味着它们有一个底层指针。普通函数调用按值复制此指针(引用),而通过引用发送发送对此引用的引用:

//C# code:
void Foo(ClassA     input)
void Bar(ClassA ref input)

//equivalent C++ code:
void Foo(ClassA*  input)
void Bar(ClassA*& input)

Primitives such as int, double, etc structs and strings(string are an exception to these, but works similar), are allocated on the heap, so things work a bit different:

诸如 int、double 等结构体和字符串(字符串是这些的例外,但工作方式类似)等基元被分配在堆上,因此工作方式略有不同:

//C# code:
void Foo(StructA     input)
void Bar(StructA ref input)

//equivalent C++ code:
void Foo(StructA  input)
void Bar(StructA& input)

the refkeyword needs to be used both in declaration of the method and when calling it, so it would be clear it is referenced:

ref关键字需要在方法的声明,呼吁时,均可使用和,所以这将是明确的引用它:

//C# code:
void Foobar(ClassB ref input)
...
ClassB instance = new ClassB();
Foobar(ref instance);

//equivalent C++ code:
void Foobar(ClassB*& input)
...
ClassB instance* = new ClassB();
Foobar(instance);

As said before, please read thisdetailed explanation. It also explains about strings.

如前所述,请阅读详细说明。它还解释了字符串。



It is interesting to note that calling by reference works with an underlying pointer, so we get to this code:



有趣的是,通过引用调用与底层指针一起工作,所以我们得到以下代码:

//C# code:
void Foo(ClassA input){
    input = input + 3;
}
void Bar(ClassA ref input){
    input = input + 3;
}
//equivalent C++ code:
void Foo(ClassA&  input){
    input = input + 3;
}
void Bar(ClassA*&  input){
    *input = *input + 3;
}
//equivalent pure C code:
void Fun(ClassA* input){
    *input = *input + 3;
}
void Fun(ClassA** input){
    *(*input) = *(*input) + 3;
}

it's a rough equivalent, but it's somewhat true.

这是一个粗略的等价物,但它有点真实。

回答by bueller

I know this is an old question, but none of the answers give a good direct reason why in my opinion.

我知道这是一个老问题,但在我看来,没有一个答案能给出一个很好的直接原因。

You don't need to use refin this instance, and here's why. Consider this function:

您不需要ref在这种情况下使用,原因如下。考虑这个函数:

void Foo(MyClass a1, ref MyClass a2, out MyClass b1, int c1, MyStruct d1, ref MyStruct d2)
{
}

Now call this function as

现在将此函数称为

MyClass  a = new MyClass();
MyClass  b = null
int      c = 3;
MyStruct d = new MyStruct();

Foo(a, ref a, b, c, d, ref d);

Here's what you get inside of the function:

这是您在函数内部获得的内容:

void Foo(MyClass a1, ref MyClass a2, 
         out MyClass b1, 
         int c1, 
         MyStruct d1, ref MyStruct d2)
{
   a1 is a copy in memory of the pointer to the instantiated class a;
   a2 is the pointer to the instantiated class a;

   b1 is the pointer to b, but has the additional check of having to be set within this function - and cannot be used before being set;

   c1 is a copy in memory of the variable c;

   d1 is a copy in memory of the struct d;
   d2 is the struct d;
}

Important things to realize:

需要意识到的重要事项:

  1. setting a1to nullwill notset ato null.
  2. setting a2to nullwill set ato null.
  3. setting b1is required.
  4. setting c1will notchange c.
  5. setting d1will notchange d.
  6. setting d2will change d.
  1. 设置a1null设置anull
  2. 设置a2null将设置anull
  3. b1需要设置。
  4. 设置c1不会改变c
  5. 设置d1不会改变d
  6. 设置d2会改变d

This allows for some weirdness like this:

这允许一些像这样的奇怪:

void Foo(MyClass x, ref MyClass y)
{
    x = null;
    y.Bar("hi");
}

Called like:

调用如下:

MyClass a = new MyClass();
Foo(a, ref a);

You are using a class, and so your situation is more like variable a1in the function call. Which means refisn't strictly required.

您正在使用一个类,因此您的情况更像a1是函数调用中的变量。这意味着ref不是严格要求的。

The Jon Skeet article won't help you that much because his example with IntHolderis a structnot a class. Structis value type like intand must be handled the same way.

Jon Skeet 的文章对你没有多大帮助,因为他的例子IntHolder是一个structnot a classStruct是值类型int,必须以相同的方式处理。