如何通过反射实现对象及子对象的实例化与更新?
反射实例化并更新包含子对象的对象的最佳实现
我需要实现一种通过反射实例化并更新包含子对象的对象的方法。当前的GetInstance函数只会创建新对象并直接覆盖原有实例,现在希望修改该函数,使其优先返回已存在的对应类型实例并更新,无实例时再创建新实例。
原GetInstance函数
public static object GetInstance(string strFullyQualifiedName) { if(strFullyQualifiedName.Split(' ').Length > 1) { strFullyQualifiedName = strFullyQualifiedName.Split(' ')[0]; } Type type = Type.GetType(strFullyQualifiedName); if (type != null) return Activator.CreateInstance(type); foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { type = asm.GetType(strFullyQualifiedName); if (type != null) return Activator.CreateInstance(type); } return null; }
示例对象定义
public class C { string C1 { get; set; } string C2 { get; set; } } public class B { public string B1 { get; set; } public string B2 { get; set; } public C c { get; set; } } public class A { public string A1 { set; get; } public string A2 { set; get; } public B b { get; set; } }
问题复现代码
当前调用GetInstance会直接创建新的B实例覆盖原有的a.b,导致原有B1的值丢失:
static void Main(string[] args) { A a = new A(); a.b = new B(); a.b.B1 = "B1"; PropertyInfo propp = a.GetType().GetProperty("b"); propp.SetValue(a, GetInstance("ConsoleApp1.B"), null); }
尝试的修改方案(未完成)
尝试给GetInstance增加参数传入父对象,遍历父对象的属性查找对应类型实例,但逻辑不完整:
// 省略原有查找类型的代码 if (type != null) { PropertyInfo[] pLIST = o.GetType().GetProperties(); foreach(PropertyInfo p in pLIST) { object oo = p.GetValue(o); if(oo != null && oo.GetType() == type) { return oo; } } return Activator.CreateInstance(type); } // 省略后续代码 // 调用方式 propp.SetValue(a, GetInstance("ConsoleApp1.B",a), null);
完整解决方案
要实现复用已有实例并更新的需求,需要调整GetInstance的逻辑:不仅要查找父对象中是否存在对应类型的实例,还要支持传入待更新的属性值,同时处理嵌套子对象的情况。以下是优化后的实现:
1. 重载GetInstance方法,支持传入父对象和待更新属性
public static object GetInstance(string strFullyQualifiedName, object parentInstance = null, Dictionary<string, object> propertyValues = null) { // 处理类型名称,移除可能的额外后缀 if(strFullyQualifiedName.Split(' ').Length > 1) { strFullyQualifiedName = strFullyQualifiedName.Split(' ')[0]; } // 查找目标类型 Type type = Type.GetType(strFullyQualifiedName); if (type == null) { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { type = asm.GetType(strFullyQualifiedName); if (type != null) break; } } if (type == null) return null; // 优先从父对象中查找已存在的同类型实例 object targetInstance = null; if (parentInstance != null) { foreach (var prop in parentInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var propValue = prop.GetValue(parentInstance); if (propValue != null && propValue.GetType() == type) { targetInstance = propValue; break; } } } // 无实例则创建新实例 if (targetInstance == null) { targetInstance = Activator.CreateInstance(type); } // 如果传入属性值,递归更新实例(包括嵌套子对象) if (propertyValues != null && propertyValues.Count > 0) { UpdateObjectProperties(targetInstance, propertyValues); } return targetInstance; } // 辅助方法:递归更新对象属性,支持嵌套子对象 private static void UpdateObjectProperties(object target, Dictionary<string, object> propertyValues) { var type = target.GetType(); foreach (var kvp in propertyValues) { var prop = type.GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance); if (prop == null) continue; // 处理嵌套子对象的更新 if (kvp.Value is Dictionary<string, object> nestedValues && prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) { var nestedInstance = prop.GetValue(target) ?? Activator.CreateInstance(prop.PropertyType); UpdateObjectProperties(nestedInstance, nestedValues); prop.SetValue(target, nestedInstance); } else { prop.SetValue(target, kvp.Value); } } }
2. 使用示例
现在可以复用已有实例并更新指定属性,不会丢失原有值:
static void Main(string[] args) { A a = new A(); a.b = new B(); a.b.B1 = "B1"; // 原有值保留 // 准备要更新的属性:只更新B2,保留B1 var bProperties = new Dictionary<string, object> { { "B2", "UpdatedB2" }, // 如需更新嵌套的C对象,可添加: // { "c", new Dictionary<string, object> { { "C1", "NewC1" } } } }; PropertyInfo propp = a.GetType().GetProperty("b"); // 传入父对象a和待更新属性字典,复用已有a.b实例并更新 propp.SetValue(a, GetInstance("ConsoleApp1.B", a, bProperties), null); // 验证结果:原有B1保留,B2被更新 Console.WriteLine(a.b.B1); // 输出 B1 Console.WriteLine(a.b.B2); // 输出 UpdatedB2 }
关键改进点
- 支持从父对象中查找已存在的同类型实例,避免创建新对象覆盖原有数据
- 增加递归属性更新逻辑,支持嵌套子对象的更新
- 保留原有的跨程序集类型查找逻辑,兼容不同程序集中的类型
内容的提问来源于stack exchange,提问作者Y.D
相关产品推荐
相关产品推荐

