You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取包含嵌套复杂类型的C#类的所有成员(属性、字段)

解决嵌套复杂类型的所有属性与字段递归遍历问题

我来帮你搞定这个嵌套类型成员遍历的痛点!你的需求是要递归获取类的所有属性和字段——包括嵌套的BuildData、SampleData、DummyData这些复杂类型里的成员,甚至集合元素的内部结构对吧?先看看你现有代码的几个核心问题:

  1. 只处理了属性,完全没碰字段:比如BuildData里的私有字段_buildData,还有其他类的字段都被忽略了;
  2. 集合处理逻辑有漏洞:你遍历了集合元素但没记录集合本身的信息,而且对于泛型集合的元素类型递归处理也不够清晰;
  3. 类型判断不够严谨:property.PropertyType.Assembly == objType.Assembly这个判断可能会误判某些跨程序集的自定义类型,或者把系统引用类型(比如List<string>)当成自定义类型处理;
  4. 空值处理不足:当属性值为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:59:24