如何获取包含嵌套复杂类型的C#类的所有成员(属性、字段)
解决嵌套复杂类型的所有属性与字段递归遍历问题
我来帮你搞定这个嵌套类型成员遍历的痛点!你的需求是要递归获取类的所有属性和字段——包括嵌套的BuildData、SampleData、DummyData这些复杂类型里的成员,甚至集合元素的内部结构对吧?先看看你现有代码的几个核心问题:
- 只处理了属性,完全没碰字段:比如
BuildData里的私有字段_buildData,还有其他类的字段都被忽略了; - 集合处理逻辑有漏洞:你遍历了集合元素但没记录集合本身的信息,而且对于泛型集合的元素类型递归处理也不够清晰;
- 类型判断不够严谨:
property.PropertyType.Assembly == objType.Assembly这个判断可能会误判某些跨程序集的自定义类型,或者把系统引用类型(比如List<string>)当成自定义类型处理; - 空值处理不足:当属性值为
null时,递归调用会直接返回,但没有明确的记录,容易遗漏信息。
改进后的递归遍历实现
下面是一个完善的方法,能同时处理属性和字段,正确解析嵌套类型、集合,还能区分系统类型和自定义类型:
首先,添加一个辅助方法判断是否需要递归的自定义类型:
private static bool IsCustomReferenceType(Type type) { // 排除值类型、字符串、数组,以及System命名空间下的系统类型 if (type.IsValueType || type == typeof(string) || type.IsArray || type.Namespace?.StartsWith("System") == true) return false; // 只处理当前程序集的自定义类型,可根据需求调整这个范围 return type.Assembly == typeof(A).Assembly; }
然后是核心的遍历方法,支持递归、字段/属性处理、集合解析:
public void PrintAllMembers(object obj, int indentLevel = 0, ref string result) { if (obj == null) { result += new string(' ', indentLevel * 4) + "Value: null" + Environment.NewLine; return; } Type objType = obj.GetType(); string indent = new string(' ', indentLevel * 4); // 处理所有字段(包括私有字段,可通过BindingFlags调整范围) FieldInfo[] fields = objType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo field in fields) { object fieldValue = null; try { fieldValue = field.GetValue(obj); } catch (Exception ex) { fieldValue = $"[Error accessing field: {ex.Message}]"; } result += $"{indent}*Field* | Name: {field.Name}, Type: {field.FieldType}, Value: {fieldValue ?? "null"}\n"; // 递归遍历自定义引用类型的字段值 if (IsCustomReferenceType(field.FieldType)) { PrintAllMembers(fieldValue, indentLevel + 1, ref result); } // 处理集合类型(排除字符串,因为string也是IEnumerable) else if (typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.FieldType != typeof(string)) { IEnumerable collection = fieldValue as IEnumerable; if (collection != null) { int itemIndex = 0; foreach (var item in collection) { result += $"{indent} → Collection Item [{itemIndex}]:\n"; PrintAllMembers(item, indentLevel + 2, ref result); itemIndex++; } } } } // 处理所有实例属性(跳过索引器) PropertyInfo[] properties = objType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { // 跳过带参数的索引器属性 if (property.GetIndexParameters().Length > 0) continue; object propValue = null; try { propValue = property.GetValue(obj); } catch (Exception ex) { propValue = $"[Error accessing property: {ex.Message}]"; } result += $"{indent}*Property* | Name: {property.Name}, Type: {property.PropertyType}, Value: {propValue ?? "null"}\n"; // 递归遍历自定义引用类型的属性值 if (IsCustomReferenceType(property.PropertyType)) { PrintAllMembers(propValue, indentLevel + 1, ref result); } // 处理集合类型 else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string)) { IEnumerable collection = propValue as IEnumerable; if (collection != null) { int itemIndex = 0; foreach (var item in collection) { result += $"{indent} → Collection Item [{itemIndex}]:\n"; PrintAllMembers(item, indentLevel + 2, ref result); itemIndex++; } } } } }
使用示例
初始化测试对象后调用方法:
// 构造测试数据 var testA = new A { Id = 1, Name = "TestClassA", Branch = new List<string> { "MainBranch", "DevBranch" }, build = new BuildData { param1 = "ReleaseBuild", paramArgs = new[] { "/out:bin", "/optimize" } }, sample = new SampleData { Test = "SampleTestValue", dummyArgs = new List<DummyData> { new DummyData { Val = 100, GUIData = "DummyInstance1" }, new DummyData { Val = 200, GUIData = "DummyInstance2" } } }, dData = new DummyData { Val = 300, GUIData = "StandaloneDummy" } }; string output = string.Empty; PrintAllMembers(testA, ref output); Console.WriteLine(output);
关键改进点说明
- 同时处理字段和属性:通过
BindingFlags控制可以获取私有字段,满足你对所有成员的需求; - 结构化输出:用缩进区分嵌套层级,输出更易读;
- 安全访问:添加异常捕获,避免因属性/字段访问权限或getter逻辑报错中断遍历;
- 精准类型判断:排除系统类型,只递归自定义复杂类型,避免不必要的递归;
- 集合处理优化:明确区分字符串和其他集合类型,遍历集合元素并递归解析内部结构。
内容的提问来源于stack exchange,提问作者Learn Avid
相关产品推荐
相关产品推荐

