如何利用反射递归比较含多层嵌套属性的同类型对象
递归反射比较嵌套对象属性值
测试场景中需要用反射比较同类型对象,对象可能包含多层嵌套属性。示例类定义如下:
public class Connection{ public string Ip{get; set;} public string Id{get; set;} public string Key{get; set;} public string Transport{get; set;} public Parameters ParametersObj = new Parameters(); public class Parameters { public string AssignedName { get; set; } public string CategoryType { get; set; } public string Status { get; set; } } }
现有实现仅能对比第一层属性,完全忽略嵌套对象的内部值。需要实现支持任意嵌套深度的比较逻辑,且属性存在差异时,日志要输出类似Property: Parameters.Status的完整属性路径。
现有实现代码:
bool result = true; foreach (var objParam in firstObj.GetType().GetProperties()) { var value1 = objParam.GetValue(firstObj); var value2 = objParam.GetValue(secondObj); if (value1 == null || value2 == null || !value1.Equals(value2)) { logger.Error("Property: " + objParam.Name); logger.Error("Values: " + value1?.ToString() + " and " + value2?.ToString()); result = false; } } return result;
改进后的递归实现
可以通过递归遍历属性的方式处理嵌套对象,同时维护当前的属性路径:
private bool CompareObjects(object firstObj, object secondObj, string currentPath = "", ISet<object> comparedObjects = null) { bool result = true; // 初始化已比较对象集合,防止循环引用导致死递归 comparedObjects ??= new HashSet<object>(); // 处理null情况 if (firstObj == null && secondObj == null) return true; if (firstObj == null || secondObj == null) { logger.Error($"Property: {currentPath}"); logger.Error($"Values: {firstObj?.ToString()} and {secondObj?.ToString()}"); return false; } // 若对象已比较过,直接返回true(避免循环引用) if (!comparedObjects.Add(firstObj)) return true; comparedObjects.Add(secondObj); Type objType = firstObj.GetType(); // 值类型或字符串直接比较 if (objType.IsValueType || objType == typeof(string)) { if (!firstObj.Equals(secondObj)) { logger.Error($"Property: {currentPath}"); logger.Error($"Values: {firstObj.ToString()} and {secondObj.ToString()}"); result = false; } return result; } // 遍历所有公开实例属性递归比较 foreach (var property in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { string propertyPath = string.IsNullOrEmpty(currentPath) ? property.Name : $"{currentPath}.{property.Name}"; var value1 = property.GetValue(firstObj); var value2 = property.GetValue(secondObj); if (!CompareObjects(value1, value2, propertyPath, comparedObjects)) { result = false; } } return result; }
关键说明
- 递归遍历:遇到引用类型(非值类型、非字符串)时,递归调用比较方法,深入嵌套对象内部
- 属性路径维护:通过
currentPath参数拼接完整的属性层级路径,差异日志直接输出完整路径 - null处理:单独判断两个对象的null情况,避免空引用异常
- 循环引用防护:使用
ISet<object>记录已比较过的对象,防止循环引用导致的死递归 - 权限控制:使用
BindingFlags.Public | BindingFlags.Instance确保遍历所有公开实例属性
内容的提问来源于stack exchange,提问作者Shmuel Fellous
相关产品推荐
相关产品推荐

