使用ref与C类#

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

我应该使用ref关键字吗?

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

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

 void A(ref LinkedList list){ B(list); } void B(ref LinkedList list){ _myList = list; } 

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

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

Jon Skeet写了一篇关于C#参数传递的优秀文章 ,它比较和对比了值类型,引用类型,传递值,传递引用( ref )和输出参数( out )。 我建议你花一些时间仔细阅读这些内容,你的理解应该变得更加清晰。

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

值参数:

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

参考参数:

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

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

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

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

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

示例#2ref关键字必要。

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

在你发布的两个片段中,没有必要通过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 } 

不,你不需要使用ref。

LinkedList是一个对象,因此它已经是一个引用类型。 参数list是对LinkedList对象的引用。

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

您可能还希望通过ref传递引用类型。 这将允许您将引用指向另一个对象。

无论何时传递object o您实际上都是在传递对象的引用。 传递`ref object o’时,您将传递对引用的引用。 这允许您修改引用。

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

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

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

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

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

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

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

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

有趣的是,通过引用调用使用底层指针,所以我们得到这个代码:

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

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

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

在这种情况下,您不需要使用ref ,这就是原因。 考虑这个function:

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

现在将此函数称为

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

这是你在函数内部得到的:

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

要实现的重要事项:

  1. a1设置为null 不会a设置为null
  2. a2设置为nulla2设置为null
  3. 设置b1是必需的。
  4. 设置c1 不会改变c
  5. 设置d1 不会改变d
  6. 设置d2将改变d

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

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

叫做:

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

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

Jon Skeet的文章对你帮助不大,因为他使用IntHolder例子是一个struct而不是一个classStruct是类似int值类型,必须以相同的方式处理。