在c#中列出

我无法理解List背后的逻辑,因为它打破了一些基本规则。

List应该是值类型而不是引用类型。

  1. 如果必须在函数调用之间保持其值,则必须通过ref关键字传递List 。 所以这意味着它正在显示类似于int的值类型行为。
  2. 但是List必须由new运算符初始化。 List也可以为null。 这意味着引用类型行为。

可空类型是不同的,因为它不必由新运算符初始化。

我在这看错了吗?

EDITED-

我应该在原始问题中发布代码。 但它遵循 –

 namespace ConsoleApplication1 { class Program { static void Main(string[] args) { ListTest d = new ListTest(); d.Test(); } } class ListTest { public void ModifyIt(List l) { l = returnList(); } public void Test() { List listIsARefType = new List(); ModifyIt(listIsARefType); Console.WriteLine(listIsARefType.Count); // should have been 1 but is 0 Console.ReadKey(true); } public List returnList() { List t = new List(); t.Add(1); return t; } } } 

List应该是值类型而不是引用类型。

错误! int是值类型。 List是引用类型。

我认为你的第一颗子弹中有一个错误的假设。 通用List对象绝对是引用类型(在堆上,而不是堆栈)。 不确定为什么你认为你必须通过ref 。 这打印“2”就像它应该:

 namespace ConsoleApplication1 { class Program { static void Main(string[] args) { List listIsARefType = new List(); ModifyIt(listIsARefType); ModifyIt(listIsARefType); Console.WriteLine(listIsARefType.Count); // 2! Console.ReadKey(true); } static void ModifyIt(List l) { l.Add(0); } } } 

您需要了解传递引用按值传递和按值 传递引用之间的区别。

在您发布的代码示例中,您将按值传递对List对象的引用 。 这意味着您可以改变引用指向的对象,并且调用代码将看到这些更改。 但是,引用本身是按值传递的,因此如果将引用更改为指向另一个对象,则调用代码将不会看到更改。

当您使用ref关键字时,您将通过引用传递引用。 这意味着您不仅可以更改引用指向的对象,还可以更改引用本身。

考虑这个例子:

 class Program { static void Main() { int foo = 0; DoSomething1(foo); Console.WriteLine(foo); // Outputs 0. DoSomething1(ref foo); Console.WriteLine(foo); // Outputs 1. var bar = new List(); DoSomething2(bar); Console.WriteLine(bar.Count); // Outputs 1. DoSomething2(ref bar); Console.WriteLine(bar.Count); // Outputs 0. } // Pass by value. static void DoSomething1(int number) { // Can't modify the number! number++; } // Pass by value. static void DoSomething1(ref int number) { // Can modify the number! number++; } // Pass reference by value. static void DoSomething2(List list) { // Can't change the reference, but can mutate the object. list.Add(25); } // Pass reference by reference. static void DoSomething2(ref List list) { // Can change the reference (and mutate the object). list = new List(); } } 

不要把它想象成List把它写成List

List是一个通用类 。 它不是结构。 它是一个可以使用值类型和引用类型的generics类。

List确实是一个引用类型。 但列表中包含的项目是值类型。

然而,可以为Nullable的类型实现为结构( struct Nullable where T : struct ),因此是值类型。 你可以简单地写的原因

 int? i = 3; 

如果没有new关键字,则上述语法会由编译器自动转换为执行以下操作的代码:

 Nullable i = new Nullable(3); 

为了更好地理解值类型和引用类型语义之间的差异,我建议您阅读Jon Skeet关于此主题的文章,该文章将为您提供大量示例代码示例:

Jon Skeet:C#中的参数传递

List是一种通用引用类型,您使用值类型为int的类型。 但它仍然是一种参考类型。

List是引用类型。 并且它不必作为参考传递。

列表中对象的类型是一种值类型,除了值类型对象最终可以装箱(转换为类型的引用类型对象),所以……一旦你理解了这一点,就抓住这一段。

除了在其他答案中处理的错误假设之外,你说:

List必须由new运算符初始化…这意味着引用类型行为。

不,在C#中, new运算符只是调用类型构造函数的语法。 它用于引用和用户定义的值类型( struct )。

在ModifyIt()方法中,当你写’l = returnList()’时; ‘l’现在指向内存中的不同位置,而不是Test()方法中listIsARefType的位置。 基本上通过写’l”=’某事,你已经打破了’l’和’listIsARefType’之间的联系。 为了保持链接(确保两个对象,’l’和’listIsARefType’指向内存中的相同位置),您需要只处理’l’对象(例如通过调用对象上的函数),或在ModifyIt()方法的参数中使用ref关键字。