复制List< T>.添加(T项)项

本文关键字:添加 List 复制 | 更新日期: 2023-09-27 18:04:40

为什么aFooList包含最后一个项目的五个副本,而不是我插入的五个项目?

期望输出:01234

实际输出:44444

using System;
using System.Collections.Generic;
namespace myTestConsole {
    public class foo {
        public int bar;
    }
    class Program {
        static void Main(string[] args) {
            foo aFoo = new foo(); // Make a foo
            List<foo> aFooList = new List<foo>(); // Make a foo list
            for (int i = 0; i<5; i++) { 
                aFoo.bar = i;
                aFooList.Add(aFoo);
            }
            for (int i = 0; i<5; i++) {
                Console.Write(aFooList[i].bar);
            }
        }
    }
}

复制List< T>.添加(T项)项

您已将同一项aFoo添加了5次。当您修改引用类型对象的内容时,您不会创建新的副本,而是修改相同的对象。

List<foo> aFooList = new List<foo>(); // Make a foo list
for (int i = 0; i<5; i++) { 
    foo aFoo = new foo(); // Make a foo
    aFoo.bar = i;
    aFooList.Add(aFoo);
}

当aFoo在列表中时,您仍在修改它。

您已经向列表中添加了对同一对象的引用5次,每次都修改bar的值。在aFooList.Add(aFoo);之后添加这一行,以查看每次向列表中添加foo时的效果。

Console.WriteLine(string.Join("", foos.Select(f => f.bar)));

无论如何,它是Linq的一行代码(为了可读性,间距很好)。

var foos = Enumerable.Range(0, 5)
                     .Select(n => new foo {bar = n})
                     .ToList();

完整示例:

foo aFoo = new foo(); // Make a foo
List<foo> aFooList = new List<foo>(); // Make a foo list
Console.WriteLine("'nUsing aFooList");
for (int i = 0; i < 5; i++)
{
    aFoo.bar = i;
    aFooList.Add(aFoo);
    Console.WriteLine(string.Join("", aFooList.Select(f => f.bar)));
}
var foos = Enumerable.Range(0, 5).Select(n => new foo { bar = n }).ToList();
Console.WriteLine("'nUsing foos");
Console.WriteLine(string.Join("", foos.Select(f => f.bar)));
Console.ReadLine();
输出:

<>之前使用aFooList011222333344444使用foo01234

变量'aFoo'基本上是指向内存中某个位置的指针。如果不"更新"另一个实例(malloc),您将修改内存中的相同位置并添加相同的指针5次。

对于c#程序员来说,"new"关键字就像malloc在C中所做的那样完成函数。它将获得一个新的内存位置,并使aFoo指向该位置,而不是旧的位置。

从MSDN:

The new operator is used to create objects and invoke constructors

MSDN