如何正确存储列表<;T>;将对象项添加到列表的另一个实例<;T>;在不影响其单个项目值的情况下-C#

如何正确存储列表<;T>;将对象项添加到列表的另一个实例<;T>;在不影响其单个项目值的情况下-C#,c#,list,class,C#,List,Class,问题是: 我有这个班的人: class Person { public int Id { get; set; } public string Name { get; set; } public string Position { get; set; } public int Age { get; set; } } 我将这个名为workersA的对象初始化为源对象: List<Person> workersA = new List<Person&

问题是:

我有这个班的

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Position { get; set; }
    public int Age { get; set; }
}
我将这个名为
workersA
的对象初始化为源对象:

List<Person> workersA = new List<Person>()
{
   new Person { Id = 1, Name = "Emp1", Age = 27  },
   new Person { Id = 2, Name = "Emp2", Age = 18  },
   new Person { Id = 3, Name = "Emp3", Age = 23  },
};
在使用
foreach
语句从源代码存储的
workersC

foreach (var w in workersA.ToList())
{
    workersC.Add(w);
}

现在,当我试图修改源对象的
Age
属性时。。代码如下:

foreach (var p in workersA.ToList())
{
    p.Age--;
}
并使用以下代码输出所有结果:

Console.WriteLine("Wokers A:");
foreach (var p in workersA.ToList())
{
    Console.WriteLine("Id: {0} Name: {1}, Age: {2}", p.Id, p.Name, p.Age);
}

Console.WriteLine();
Console.WriteLine("Wokers B:");
foreach (var p in workersB.ToList())
{
    Console.WriteLine("Id: {0} Name: {1}, Age: {2}", p.Id, p.Name, p.Age);
}

Console.WriteLine();
Console.WriteLine("Wokers B:");
foreach (var p in workersB.ToList())
{
    Console.WriteLine("Id: {0} Name: {1}, Age: {2}", p.Id, p.Name, p.Age);
}
这是我得到的:

Wokers A:
Id: 1 Name: Emp1, Age: 26
Id: 2 Name: Emp2, Age: 17
Id: 3 Name: Emp3, Age: 22

Wokers B:
Id: 1 Name: Emp1, Age: 26   // 27 - expectation
Id: 2 Name: Emp2, Age: 17   // 18 - expectation
Id: 3 Name: Emp3, Age: 22   // 22 - expectation

Wokers C:
Id: 1 Name: Emp1, Age: 26   //  27 - expectation
Id: 2 Name: Emp2, Age: 17   //  18 - expectation
Id: 3 Name: Emp3, Age: 22   //  22 - expectation
清楚地回答这个问题

为什么我得到了相同的输出


因为,归根结底,
workersB
workersC
中的每个元素都将指向您最初创建的单个
Person
实例。因此,它们都引用相同的实例-因此得到了相同的结果

简而言之,您得到了三个不同的列表,其中包含三个相同的
Person
实例

执行此操作时:

workersB = workersA.ToList();
foreach (var w in workersA.ToList())
{
    workersC.Add(w);
}
您可以创建
列表的新实例,而不是列表中单个对象的新实例。同样,当您这样做时:

workersB = workersA.ToList();
foreach (var w in workersA.ToList())
{
    workersC.Add(w);
}
您正在将
workersA
中的相同
Person
引用传递给
workersC
。但它们都是指相同的
Person
实例

因此,当你最终做到这一点时:

foreach (var p in workersA.ToList())
{
    p.Age--;
}
每个
实例的
年龄
都会发生变化,尽管其中包含三个不同的列表


解决方案:

如果您确实想将不同的
人员
传递给三个列表,一种方法是为
人员
实现一个
克隆
方法(您可以在
IClonable
界面中找到):

然后,您可以使用
克隆
而不是原始的
人员
输入其他列表:

foreach (var w in workersA.ToList())
{
    workersC.Add(w.Clone()); //use Clone here
}

根据


因为,归根结底,
workersB
workersC
中的每个元素都将指向您最初创建的单个
Person
实例。因此,它们都引用相同的实例-因此得到了相同的结果

简而言之,您得到了三个不同的列表,其中包含三个相同的
Person
实例

执行此操作时:

workersB = workersA.ToList();
foreach (var w in workersA.ToList())
{
    workersC.Add(w);
}
您可以创建
列表的新实例,而不是列表中单个对象的新实例。同样,当您这样做时:

workersB = workersA.ToList();
foreach (var w in workersA.ToList())
{
    workersC.Add(w);
}
您正在将
workersA
中的相同
Person
引用传递给
workersC
。但它们都是指相同的
Person
实例

因此,当你最终做到这一点时:

foreach (var p in workersA.ToList())
{
    p.Age--;
}
每个
实例的
年龄
都会发生变化,尽管其中包含三个不同的列表


解决方案:

如果您确实想将不同的
人员
传递给三个列表,一种方法是为
人员
实现一个
克隆
方法(您可以在
IClonable
界面中找到):

然后,您可以使用
克隆
而不是原始的
人员
输入其他列表:

foreach (var w in workersA.ToList())
{
    workersC.Add(w.Clone()); //use Clone here
}

根据


由于类Person是reference type(),您的列表只包含对最初创建的Person实例的引用。当您更改任何列表中的实例时,每个列表中的实例都会更改。

由于类Person是引用类型(),您的列表仅包含对最初创建的Person实例的引用。当您更改任何列表中的实例时,每个列表中的实例都会更改。

您会得到相同的输出,因为您的
Person
类是引用类型。因此,三个列表中的每一个都包含对相同对象的引用。因此,如果更改其中一个,那么也会更改其他两个列表中的相应元素,因为它们指向同一个元素。如果希望
workersB
workersC
包含独立元素,则必须复制它们。例如,添加一个副本构造函数

class Person {
   ....

   public Person(Person other) {
       this.Id = other.Id;
       this.Name = other.Name;
       ...
   }
}
当向
workersB
workersC
添加元素时,调用复制构造函数

 foreach (Person p in workersA) {
     workersB.Add(new Person(p));
     workersC.Add(new Person(p));
 }
或者作为另一种选择

 workersB = workersA.Select(x=> new Person(x)).ToList();
 workersC = workersA.Select(x=> new Person(x)).ToList();

这样,所有元素都是新对象,彼此不相关。

由于您的
Person
类是引用类型,因此您将获得相同的输出。因此,三个列表中的每一个都包含对相同对象的引用。因此,如果更改其中一个,那么也会更改其他两个列表中的相应元素,因为它们指向同一个元素。如果希望
workersB
workersC
包含独立元素,则必须复制它们。例如,添加一个副本构造函数

class Person {
   ....

   public Person(Person other) {
       this.Id = other.Id;
       this.Name = other.Name;
       ...
   }
}
当向
workersB
workersC
添加元素时,调用复制构造函数

 foreach (Person p in workersA) {
     workersB.Add(new Person(p));
     workersC.Add(new Person(p));
 }
或者作为另一种选择

 workersB = workersA.Select(x=> new Person(x)).ToList();
 workersC = workersA.Select(x=> new Person(x)).ToList();

这样,所有元素都是新对象,彼此不相关。

为了隔离结果,需要创建单独的实例。实现的方法之一是在创建新列表时克隆实例

看看这个答案,它涵盖了相当全面的领域:


为了隔离结果,需要创建单独的实例。实现的方法之一是在创建新列表时克隆实例

看看这个答案,它涵盖了相当全面的领域:


@ShiftN'Tab啊,是的,你应该创建三个
人的副本/克隆,而不是制作三个包含相同
人的列表。一种简单的方法是,让
Person
拥有
Clone
方法,每次根据其自身属性创建新的
Person
,然后通过克隆的
Person
而不是原始的
Person
将对其进行测试