如何使用反射获取类及嵌套对象中所有名为Id的Guid属性及对应值
基于递归反射的实现方案
以下是可直接运行的实现,支持任意层级嵌套、泛型集合类型,自动过滤不符合要求的属性:
using System.Reflection; using System.Collections; public static class IdCollector { // 存储已扫描过的类型的属性信息,避免重复反射查询,提升性能 private static readonly Dictionary<Type, List<PropertyInfo>> _typePropertyCache = new(); /// <summary> /// 递归收集对象树中所有名称为Id的Guid类型属性值 /// </summary> /// <param name="target">要扫描的根对象实例</param> /// <returns>所有符合要求的Guid Id集合</returns> public static List<Guid> CollectAllGuidIds(object target) { var result = new List<Guid>(); RecursiveCollect(target, result); return result; } private static void RecursiveCollect(object? target, List<Guid> result) { // 空对象直接跳过 if (target == null) return; Type targetType = target.GetType(); // 第一步:先处理当前对象的Id属性 var idProp = targetType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance); if (idProp != null && idProp.PropertyType == typeof(Guid) && idProp.CanRead) { var idValue = (Guid)idProp.GetValue(target)!; result.Add(idValue); } // 第二步:获取当前类型的所有公共实例属性,优先读缓存 if (!_typePropertyCache.TryGetValue(targetType, out var properties)) { properties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList(); _typePropertyCache[targetType] = properties; } foreach (var prop in properties) { // 跳过已处理的Id属性、不可读的属性 if (prop.Name == "Id" || !prop.CanRead) continue; var propValue = prop.GetValue(target); if (propValue == null) continue; // 处理集合类型(排除字符串,因为字符串实现了IEnumerable<char>) if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) && prop.PropertyType != typeof(string)) { var enumerator = ((IEnumerable)propValue).GetEnumerator(); while (enumerator.MoveNext()) { RecursiveCollect(enumerator.Current, result); } } // 处理自定义类类型,跳过基础值类型、枚举、字符串等不需要递归的类型 else if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) { RecursiveCollect(propValue, result); } } } }
调用示例
// 构造测试数据 var main = new Main { Id = Guid.NewGuid(), Children = new List<Child> { new Child { Id = Guid.NewGuid(), GrandChildren = new List<GrandChild> { new GrandChild{ Id = Guid.NewGuid() }, new GrandChild{ Id = Guid.NewGuid() } } }, new Child{ Id = Guid.NewGuid() } } }; // 收集所有Id var allIds = IdCollector.CollectAllGuidIds(main); // 最终输出结果共5个Id:1个Main类Id、2个Child类Id、2个GrandChild类Id
可选优化说明
- 如果你需要保留Id对应的对象层级路径,可以把返回值改成
List<(string Path, Guid Id)>,递归的时候拼接路径信息即可 - 内置的类型属性缓存可以大幅降低高频调用场景下的反射开销
- 支持所有实现了
IEnumerable接口的泛型/非泛型集合,无需单独适配 - 自动处理空值场景,不会触发空引用异常
内容的提问来源于stack exchange,提问作者lolo xoxo
相关产品推荐
相关产品推荐

