用其他列表初始化的通用列表是否复制值或引用
本文关键字:列表 引用 复制 其他 初始化 是否 | 更新日期: 2023-09-27 17:54:04
如果我有一个List<T> Foo
,并且通过将Foo
传递给Bar
的构造函数来初始化另一个List<T> Bar
,那么Bar
是否可以访问Foo
中的原始对象?还是Bar
中的对象是单独的副本?
这里有一个愚蠢的例子:
class Car
{
public string Make { get; private set; }
public string Model { get; private set; }
public string Year { get; private set; }
public int FuelLevel { get; private set; } = 0;
public int OilLevel { get; private set; } = 0;
public Car(string make, string model, string year)
{
Make = make;
Model = model;
Year = year;
}
public void Refuel()
{
FuelLevel = 100;
}
}
class Program
{
public static void Main(string[] args)
{
List<Car> CarsThatJoeOwns = new List<Car> { new Car("Ford", "Explorer", "2005"),
new Car("Hyundai", "Elantra", "2011") };
// For some reason, Paul owns the exact same types of cars that Joe owns...
List<Car> CarsThatPaulOwns = new List<Car> (CarsThatJoeOwns);
foreach (Car car in CarsThatPaulOwns)
{
car.Refuel(); // <---- does this affect the cars that Joe owns too?
}
}
}
类Car
是一个引用类型,因此,将发生以下情况:
var car = new Car("Ford", "Explorer", "2005");
var carReference = car;
carReference.Refuel();
//Will have the value of 100, even if no method was called in the car
//object, but because it is a reference type, calling Refuel method
//on carReference will also affect to the variable referenced by
//carReference (car)
var fuelLevel = car.FuelLevel
在您的代码示例中,您有一个特定的部分:
List<Car> CarsThatJoeOwns = new List<Car>
{
new Car("Ford", "Explorer", "2005"),
new Car("Hyundai", "Elantra", "2011")
};
// For some reason, Paul owns the exact same types of cars that Joe owns...
List<Car> CarsThatPaulOwns = new List<Car> (CarsThatJoeOwns);
分配CarsThatPaulOwns
时调用的List<T>
构造函数将已经存在的列表CarsThatJoeOwns
作为参数,该列表实现了接口ICollection<T>
,因此CarsThatJoeOwns
可以被广播到ICollection<T>
。
此外,请查看泛型List类的构造函数的源代码:
public List(IEnumerable<T> collection) {
if (collection==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
Contract.EndContractBlock();
ICollection<T> c = collection as ICollection<T>;
if( c != null) {
int count = c.Count;
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
}
else {
_size = 0;
_items = _emptyArray;
// This enumerable could be empty. Let Add allocate a new array, if needed.
// Note it will also go to _defaultCapacity first, not 1, then 2, etc.
using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Add(en.Current);
}
}
}
}
因为CarsThatJoeOwns
可以被广播到ICollection<T>
,所以行c.CopyTo(_items, 0);
将被执行,并且它执行以下操作(如Microsoft参考源中所示(:
public void CopyTo(T[] array, int arrayIndex) {
// Delegate rest of error checking to Array.Copy.
Array.Copy(_items, 0, array, arrayIndex, _size);
}
而且,正如MSDN文档中所述,Array.Copy将执行以下操作:
备注。。。如果sourceArray和destinationArray都是引用类型的数组,或者两者都是Object类型的数组执行复制。数组的浅层副本是新数组包含对与原始数组相同的元素的引用。这个元素本身或元素引用的任何内容都不是复制。相反,数组的深层副本复制元素和元素直接或间接引用的所有内容。
请注意"数组的浅副本是一个新数组,包含对与原始数组相同元素的引用">部分。简单地说,新列表CarsThatPaulOwns
将保存对CarsThatJoeOwns
列表中已经存在的对象的引用,foreach
循环由定义
foreach (Car car in CarsThatPaulOwns)
{
car.Refuel();
}
也会影响CCD_ 21列表中的值。反之亦然(在CarsThatJoeOwns
列表上调用换料方法也会影响CarsThatPaulOwns
列表(。
这是List<T>
的构造函数
public List(IEnumerable<T> collection) {
if (collection==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
Contract.EndContractBlock();
ICollection<T> c = collection as ICollection<T>;
if( c != null) {
int count = c.Count;
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
}
else {
_size = 0;
_items = _emptyArray;
// This enumerable could be empty. Let Add allocate a new array, if needed.
// Note it will also go to _defaultCapacity first, not 1, then 2, etc.
using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Add(en.Current);
}
}
}
}
如果集合可以是ICollection<T>
,那么它将被复制到一个新的数组中。
如果没有,则执行Add
:
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
这两个实例看起来都是在复制项的引用。但是,传递给构造函数的数组(也称为Foo(已丢失其引用。这意味着,对第一个列表的任何更新都不会更新传入的列表。
是。在您的情况下,新列表将参照现有的Car对象进行初始化。因此,调用Refuel
函数将导致Paul和Joe两辆车都被修改(因为它们是同一个对象(。