Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将ref与类C一起使用#_C#_Pointers_Reference_Keyword - Fatal编程技术网

C# 将ref与类C一起使用#

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

我想给我正在制作的一个类提供一个特定的链表。我希望类写入该列表(例如by.addLast())

我应该使用
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