为什么给 cl1 赋值后,List<T> 中对应的元素仍为 null?
l_my.Add(cl1) 保存的是调用时 cl1 的引用值。当时 cl1 为 null,列表中添加的也就是 null。之后执行 cl1 = new MyCl1();,只会改变字段 cl1 的值,已经存入列表的元素不会跟着更新。
为什么引用类型也会出现这种情况?
引用类型变量保存的是对象的引用。赋值、传参或添加到列表时,复制的都是引用值,不会复制对象本身。
cl1 和 l_my[0] 是两个独立的存储位置。List<T>.Add(T item) 按值接收参数,因此列表元素与字段之间没有自动同步关系。
| 执行步骤 | cl1 的值 | l_my[0] 的值 |
|---|---|---|
| 字段默认初始化后 | null | 尚无元素 |
l_my.Add(cl1) 后 | null | null |
init_cl1() 后 | 指向新建的 MyCl1 对象 | null |
下面这条语句会创建一个对象,并把它的引用赋给 cl1:
cl1 = new MyCl1();
它没有给 l_my[0] 重新赋值,所以列表元素仍然是 null。接着从列表中读取这个元素,得到的 my2 也为 null:
MyCl1 my2 = l_my[0];
如何让列表保存初始化后的对象?
先创建对象,再添加到列表
把 init_cl1() 放在 Add 之前,就能让字段和列表元素引用同一个对象:
public Form1()
{
InitializeComponent();
List<MyCl1> l_my = new List<MyCl1>();
init_cl1();
l_my.Add(cl1);
MyCl1 my2 = l_my[0];
// my2 与 cl1 引用同一个对象
// object.ReferenceEquals(cl1, my2) 为 true
}
此时,Add 复制的是新建对象的引用,字段和列表元素都能访问这个对象。
如果已经添加了 null,就显式更新列表元素
如果业务流程要求先添加元素、后创建对象,就需要在初始化完成后,给列表中的对应位置重新赋值:
List<MyCl1> l_my = new List<MyCl1>();
l_my.Add(cl1); // 此时添加的是 null
init_cl1();
l_my[0] = cl1; // 将新对象的引用写入列表
MyCl1 my2 = l_my[0];
// my2 与 cl1 引用同一个对象
l_my[0] = cl1 会更新已有元素。如果再次调用 l_my.Add(cl1),则会在列表末尾追加一个元素,原来的第一个元素仍然是 null。
修改对象内容与重新赋值有什么区别?
当两个位置保存同一个对象的引用时,通过其中一个引用修改对象内容,另一个引用也能读到修改后的内容。
例如,给 MyCl1 添加一个属性:
public class MyCl1
{
public int Value { get; set; }
}
然后执行以下代码:
MyCl1 cl1 = new MyCl1();
List<MyCl1> l_my = new List<MyCl1>();
l_my.Add(cl1);
cl1.Value = 10;
// l_my[0].Value 为 10
cl1 = new MyCl1 { Value = 20 };
// cl1.Value 为 20
// l_my[0].Value 仍为 10
cl1.Value = 10 修改的是两个引用所指向的同一个对象。之后执行 cl1 = new MyCl1 { Value = 20 };,cl1 指向了另一个对象,列表元素仍然引用原来的对象。
注意事项
原代码中的 MyCl1 cl1; 声明的是实例字段。引用类型字段的默认值为 null,因此可以在显式赋值前读取。如果把同样的声明写在方法内,cl1 就是局部变量,必须先赋值再使用,否则会出现编译错误。
null 表示没有引用任何对象,并不是一个等待初始化的对象。列表元素为 null 时,没有对象可供修改;需要先创建对象,再把引用赋给要使用它的字段或列表元素。
备注:内容仅供参考。
