C# 将ref与类C一起使用#
我想给我正在制作的一个类提供一个特定的链表。我希望类写入该列表(例如by.addLast()) 我应该使用C# 将ref与类C一起使用#,c#,pointers,reference,keyword,C#,Pointers,Reference,Keyword,我想给我正在制作的一个类提供一个特定的链表。我希望类写入该列表(例如by.addLast()) 我应该使用ref关键字吗 我对C#中的ref和out关键字在何处使用感到有些困惑,因为所有类都是在堆上动态分配的,我们实际上对大多数操作都使用指针。 当然,out和ref关键字对于原语和结构是有意义的 另外,如果我不直接发送列表,而是发送一个包含该列表的类?(它是internal并且是必需的),我还需要使用ref?或者如果我在函数之间传递它,例如: void A(ref LinkedList<i
ref
关键字吗
我对C#中的ref
和out
关键字在何处使用感到有些困惑,因为所有类都是在堆上动态分配的,我们实际上对大多数操作都使用指针。当然,
out
和ref
关键字对于原语和结构是有意义的
另外,如果我不直接发送列表,而是发送一个包含该列表的类?(它是internal
并且是必需的),我还需要使用ref
?或者如果我在函数之间传递它,例如:
void A(ref LinkedList<int> list){
B(list);
}
void B(ref LinkedList<int> list){
_myList = list;
}
void A(参考链接列表){
B(名单);
}
无效B(参考链接列表){
_myList=列表;
}
不,您不需要使用ref
LinkedList是一个对象,因此它已经是引用类型。参数list
是对LinkedList对象的引用
有关值类型的说明,请参见此。值类型通常是使用ref
或out
关键字的参数
您可能还希望通过ref
传递引用类型。这将允许您将引用指向另一个对象
每当你传递一个对象o
时,你实际上是在传递一个对该对象的引用。当您传递一个'ref object o'时,您正在传递一个对该引用的引用。这允许您修改引用
也可能有助于您理解。这是对C#中使用
ref
关键字的常见误解。它的目的是通过引用传递值或引用类型,并且只有在需要直接引用实际参数的特定情况下才需要它,而不是参数的副本(无论是值还是引用本身)。在任何情况下都不能将引用类型与按引用传递混淆
Jon Skeet写过关于C#中的参数传递的文章,其中比较和对比了值类型、引用类型、按值传递、按引用传递(ref
)和输出参数(out
)。我建议你花点时间通读全文,你的理解应该会更清晰
要引用该页中最重要的部分:
值参数:
默认情况下,参数为值
参数。这意味着一个新的
存储位置是为
函数成员中的变量
声明,它从
在中指定的值
函数成员调用。如果你
改变那个值,它不会改变
所有涉及的变量
调用
参考参数:
引用参数不通过
中使用的变量的值
函数成员调用-它们使用
变量本身。而不是
为创建新的存储位置
函数成员中的变量
声明,相同的存储位置
已使用,因此变量的值
在函数成员和值中
引用参数的
都一样。参考参数需要
ref修饰符作为
声明和调用-即
这意味着当你
通过引用传递某物。让我们
请看我们前面的示例,仅此而已
将参数更改为
参考参数:
总结:在阅读了我的回复和Jon Skeet的文章之后,我希望您会发现,在您的问题中没有必要使用
ref
关键字。对于您正在做的事情,您不需要使用ref。如果您确实使用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与引用类型一起使用 示例#1:
ref
关键字不是必需的
// ...
List myList = new List();
PopulateList(myList);
// ...
void PopulateList(List AList)
{
AList.Add("Hello");
AList.Add("World");
}
// ...
List myList;
PopulateList(ref myList);
// ...
void PopulateList(ref List AList)
{
AList = new List();
AList.Add("Hello");
AList.Add("World");
}
示例2:ref
关键字必需
// ...
List myList = new List();
PopulateList(myList);
// ...
void PopulateList(List AList)
{
AList.Add("Hello");
AList.Add("World");
}
// ...
List myList;
PopulateList(ref myList);
// ...
void PopulateList(ref List AList)
{
AList = new List();
AList.Add("Hello");
AList.Add("World");
}
<>我为C++程序员添加了这个答案,就像我自己一样。 类、接口、委托和数组都是
引用类型
,这意味着它们有一个底层指针。普通函数调用通过值复制此指针(引用),而通过引用发送对此引用的引用:
//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
,原因如下。考虑这个函数:
void Foo(MyClass a1, ref MyClass a2, out MyClass b1, int c1, MyStruct d1, ref MyStruct d2)
{
}
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;
}
现在将此函数称为
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)
{
}
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;
}
需要实现的重要事项:
a1
设置为null
将不将a
设置为null
a2
设置为null
将a
设置为null