名单通过裁判-帮助我解释这种行为

请看以下节目:

class Test
{
List<int> myList = new List<int>();


public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);


ChangeList(myList);


foreach (int i in myList)
{
Console.WriteLine(i);
}
}


private void ChangeList(List<int> myList)
{
myList.Sort();


List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);


myList = myList2;
}
}

我假设 myList将通过 ref,并且输出将

3
4

该列表确实是“由 ref 传递的”,但是只有 sort函数生效。以下语句 myList = myList2;无效。

所以输出实际上是:

10
50
100

你能帮我解释一下这种行为吗?如果 myList确实不是 通过裁判(因为它似乎从 myList = myList2不起作用) ,myList.Sort()如何起作用?

我甚至假设这一声明不会生效,结果是:

100
50
10
138639 次浏览

你传递了一个 参考名单,但是你的 没有传递了列表变量 参考资料-所以当你调用 ChangeList的时候,变量的值(即引用-思考“指针”)被复制了-并且 TestMethod看到 ChangeList 没有内部的 参数的值发生了变化。

尝试:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

然后传递 对局部变量的引用 myRef(如在 TestMethod中声明的那样) ; 现在,如果在 ChangeList中重新分配参数,那么也将重新分配变量 在里面 TestMethod

使用 ref关键字。

查看确定的引用 给你来理解传递参数。
具体来说,查看 这个,了解代码的行为。

编辑: Sort在相同的引用上工作(通过值传递) ,因此值是有序的。但是,为参数分配一个新实例将不起作用,因为参数是通过值传递的,除非您将 ref

在您的示例中,通过放置 ref,可以更改指向 List新实例的引用的指针。如果没有 ref,您可以处理现有的参数,但是不能让它指向其他参数。

这个 链接将帮助您理解 C # 中的逐个引用传递。 基本上,当引用类型的对象通过值传递给方法时,只有该对象上可用的方法才能修改对象的内容。

例如,List.sort ()方法更改 List 内容,但是如果将其他一些对象赋值给同一个变量,则该赋值对于该方法是局部的。这就是为什么 myList 保持不变的原因。

如果我们通过使用 ref 关键字传递引用类型的对象,那么我们可以将其他一些对象赋给同一个变量,这会改变整个对象本身。

(编辑: 这个是上面链接的文档的更新版本。)

这里有一个简单的方法来理解它

  • List 是在堆上创建的对象 引用该对象。

  • 在 C # 中,你从不传递对象,而是通过值来传递它们的引用。

  • 中传递的引用访问列表对象时 ChangeList(例如,在排序时)更改了原始列表

  • ChangeList方法上的赋值是对引用的值进行的,因此不会对原始列表进行任何更改(仍在堆中,但不再对方法变量进行引用)。

C # 只是在通过值传递时执行浅表复制,除非有问题的对象执行 ICloneable(显然 List类没有执行 ICloneable)。

这意味着它将复制 List本身,但是对列表中对象的引用保持不变; 也就是说,指针继续引用与原 List相同的对象。

如果您更改了新 List引用的对象的值,那么您也更改了原始 List(因为它引用的是相同的对象)。然而,您随后将 myList引用的内容完全更改为一个新的 List,现在只有原来的 List引用这些整数。

阅读 关于“传递参数”的 MSDN 文章中的 传递引用类型参数部分以获得更多信息。

“ How do I Clone a Generic List in C #” from StackOverflow 讲述了如何创建一个 List 的深拷贝。

虽然我同意大家上面所说的,但是我对这个代码有不同的看法。 基本上,您要将新列表分配给局部变量 myList,而不是全局变量。 如果您将 ChangeList (ListmyList)的签名更改为 private void ChangeList () ,您将看到3,4的输出。

我的理由是..。 即使 list 是通过引用传递的,也可以把它看作是通过值传递指针变量 当您调用 ChangeList (myList)时,您正在传递指向(Global) myList 的指针。现在,它存储在(本地) myList 变量中。现在您的(本地) myList 和(全局) myList 都指向同一个列表。 现在执行 sort = > ,它可以工作,因为(本地) myList 正在引用原始(全局) myList 接下来,创建一个新列表,并将指针指向您的(本地) myList。但是一旦函数退出(本地) myList 变量就会被销毁。 高温

class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{


myList.Add(100);
myList.Add(50);
myList.Add(10);


ChangeList();


foreach (int i in myList)
{
Console.WriteLine(i);
}
}


private void ChangeList()
{
myList.Sort();


List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);


myList = myList2;
}
}

最初,它可以用图形表示如下:

Init states

然后,应用排序 myList.Sort(); Sort collection

最后,当执行以下操作时: myList' = myList2,您丢失了引用之一,但没有丢失原始引用,并且集合保持排序。

Lost reference

如果通过引用(ref)使用,那么 myList'myList将变得相同(只有一个引用)。

注意: 我使用 myList'来表示您在 ChangeList中使用的参数(因为您给出了与原始参数相同的名称)

为引用类型的对象分配了两部分内存。一个在堆栈中,一个在堆中。堆栈中的部分(又名指针)包含对堆中部分的引用——实际值存储在堆中。

当不使用 ref 关键字时,只创建堆栈中部分的一个副本,并将其传递给方法引用到堆中的相同部分。因此,如果您在堆部分更改了某些内容,那些更改将保持不变。如果您更改复制的指针-通过赋值它以引用堆中的其他位置-它不会影响到方法外部的原点指针。