如果我有List<T> Foo
并通过将List<T> Bar
传递给Foo
的构造函数来初始化另一个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?
}
}
}
答案 0 :(得分:2)
这是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++;
}
两个实例看起来都在复制项目的引用。但是,传递给构造函数的数组(a.k.a Foo)已丢失其引用。这意味着,对第一个列表的任何更新都不会更新传入的列表。
答案 1 :(得分:1)
是。在您的情况下,将参考现有的Car对象初始化新列表。所以调用Refuel
函数将导致Paul和Joe汽车被修改(因为它们是同一个对象)。
答案 2 :(得分:1)
类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);
分配List<T>
时调用的CarsThatPaulOwns
构造函数将现有列表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类型的数组,浅层 执行复制。数组的浅表副本是一个新数组 包含与原始Array相同元素的引用。该 元素本身或元素引用的任何东西都不是 复制。相反,Array的深层副本会复制元素和 由元素直接或间接引用的所有内容。
请注意“数组的浅表副本是一个新数组,其中包含对与原始数组相同的元素的引用”部分。简而言之,新列表CarsThatPaulOwns
将保存对CarsThatJoeOwns
列表中已存在的对象的引用,以及{:1}}循环定义:
foreach
也会影响foreach (Car car in CarsThatPaulOwns)
{
car.Refuel();
}
列表中的值。反之亦然的情况也是如此(在CarsThatJoeOwns
列表上调用加油方法也会影响CarsThatJoeOwns
列表。)