如何避免列表项引用对象?如何复制对象防止列表项更新?
问题:如何让列表不随obj值更新,或是如何复制obj?
你的问题核心在于列表里存储的是对象引用,而非对象本身——你整个循环都在复用同一个obj实例,每次修改它的属性后添加到列表,本质上列表里所有项指向的都是内存里同一个对象,所以后续修改obj的值,列表里的项自然会跟着变。下面给你两种可行的解决思路:
方案一:每次循环创建新的对象实例(推荐)
既然你是从XML读取数据生成新对象,完全没必要复用传入的obj。我们可以给泛型加一个new()约束(要求T必须有无参构造函数),然后在每次循环里创建一个新的T实例,修改它的属性后再加入列表。
修改后的代码如下:
public static void ReadData<T>(string filename, string node) where T : new() { var xmlDocument = new XmlDocument(); xmlDocument.Load(filename); List<T> objectList = new List<T>(); XmlNodeList xmlNodeList = xmlDocument.SelectNodes("//" + node); for (var i = 0; i < xmlNodeList?.Count; i++) { // 每次循环创建新的T实例 T newObj = new T(); var j = 0; foreach (var objectProperty in newObj.GetType().GetProperties()) { if (objectProperty.CanRead && objectProperty.CanWrite) // 确保属性可写,避免赋值报错 { object value; if (objectProperty.PropertyType == typeof(bool)) { value = bool.Parse(xmlNodeList[i].ChildNodes[j].InnerText); } else { value = xmlNodeList[i].ChildNodes[j].InnerText; } objectProperty.SetValue(newObj, value); j++; } } objectList.Add(newObj); } // 可根据需求返回objectList或做后续处理 }
我去掉了原方法里的T obj参数(因为现在每次创建新实例,不需要模板对象了),同时加了objectProperty.CanWrite的判断,避免给只读属性赋值时抛出异常。
方案二:实现对象的深拷贝(如果需要复用原obj的结构/初始值)
如果你确实需要基于传入的obj复制一个新对象(比如obj有一些初始配置需要保留),可以实现一个通用的深拷贝方法。这里先提供反射复制的实现(适合简单对象):
// 通用的对象浅拷贝方法(如果属性都是值类型,足够用) private static T DeepCopy<T>(T source) where T : new() { T copy = new T(); foreach (var prop in source.GetType().GetProperties()) { if (prop.CanRead && prop.CanWrite) { var value = prop.GetValue(source); prop.SetValue(copy, value); } } return copy; }
然后在原代码里,每次循环先拷贝obj得到新实例,再修改属性:
public static void ReadData<T>(string filename, T obj, string node) where T : new() { var xmlDocument = new XmlDocument(); xmlDocument.Load(filename); List<T> objectList = new List<T>(); XmlNodeList xmlNodeList = xmlDocument.SelectNodes("//" + node); for (var i = 0; i < xmlNodeList?.Count; i++) { // 先拷贝原obj得到新实例 T newObj = DeepCopy(obj); var j = 0; foreach (var objectProperty in newObj.GetType().GetProperties()) { if (objectProperty.CanRead && objectProperty.CanWrite) { object value; if (objectProperty.PropertyType == typeof(bool)) { value = bool.Parse(xmlNodeList[i].ChildNodes[j].InnerText); } else { value = xmlNodeList[i].ChildNodes[j].InnerText; } objectProperty.SetValue(newObj, value); j++; } } objectList.Add(newObj); } }
如果你的对象包含复杂的引用类型属性,反射拷贝属于浅拷贝(只会复制引用,不会复制引用对象的内容),这种情况下可以用序列化反序列化实现深拷贝,比如用Json.NET:
using Newtonsoft.Json; private static T DeepCopy<T>(T source) { var json = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject<T>(json); }
这种方法需要你的T类型支持序列化(比如标记[Serializable]特性,或者确保所有属性都能被Json.NET序列化)。
总结
对于你从XML读取数据生成对象列表的场景,方案一是最直接高效的——每次创建新实例,完全避免了引用共享的问题。方案二适合需要基于已有对象复制的场景。
内容的提问来源于stack exchange,提问作者Benni To
相关产品推荐
相关产品推荐

