Intereting Posts

.NET, C #, Reflection: укажите поля поля, которое, собственно, имеет поля

В .NET & C # предположим, что ClassB имеет поле типа ClassA . Можно легко использовать метод GetFields для отображения полей ClassB . Тем не менее, я хочу также перечислить поля тех полей ClassB , у которых есть поля. Например, поле ClassB имеет поля b , s и i . Я хотел бы (программно) перечислить эти поля (как было предложено моими комментариями в коде ниже).

 class ClassA { public byte b ; public short s ; public int i ; } class ClassB { public long l ; public ClassA x ; } class MainClass { public static void Main ( ) { ClassA myAObject = new ClassA () ; ClassB myBObject = new ClassB () ; // My goal is this: // ***Using myBObject only***, print its fields, and the fields // of those fields that, *themselves*, have fields. // The output should look like this: // Int64 l // ClassA x // Byte b // Int16 s // Int32 i } } 

    Используйте FieldInfo.FieldType чтобы отразить тип полей в вашем classе. Например

     fieldInfo.FieldType.GetFields(); 

    Вот полный образец, основанный на вашем коде, который использует рекурсию, если у вас есть ClassZ внутри ClassA . Он разбивается, если у вас есть циклический граф объектов.

     using System; using System.Reflection; class ClassA { public byte b; public short s; public int i; } class ClassB { public long l; public ClassA x; } class MainClass { public static void Main() { ClassB myBObject = new ClassB(); WriteFields(myBObject.GetType(), 0); } static void WriteFields(Type type, Int32 indent) { foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { Console.WriteLine("{0}{1}\t{2}", new String('\t', indent), fieldInfo.FieldType.Name, fieldInfo.Name); if (fieldInfo.FieldType.IsClass) WriteFields(fieldInfo.FieldType, indent + 1); } } } 

    Класс, который делает это, уже существует! Взгляните на образцы Microsoft C # для Visual Studio: http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=csharpsamples&ReleaseId=8

    В частности, посмотрите на образец ObjectDumper, так как он n-levels глубоко. Например:

     ClassB myBObject = new ClassB(); ... ObjectDumper.Write(myBObject, Int32.MaxValue); //Default 3rd argument value is Console.Out, but you can use //any TextWriter as the optional third argument 

    Он уже учитывает, был ли посещен объект в графике, типы значений по сравнению с типами объектов по сравнению с перечисляемыми типами и т. Д.

    Попробуйте следующее. Он позволяет вам контролировать, насколько глубоко вы спускаетесь в иерархию типов, и должны только спускаться в не примитивные типы.

     public static class FieldExtensions { public static IEnumerable GetFields( this Type type, int depth ) { if( depth == 0 ) return Enumerable.Empty(); FieldInfo[] fields = type.GetFields(); return fields.Union(fields.Where( fi => !fi.IsPrimitive ) .SelectMany( f => f.FieldType.GetFields( depth -1 ) ); } } 

    Вам нужно написать рекурсивный метод, который принимает объект, проходит через его поля ( obj.GetType().GetFields() ) и печатает значение поля примитивного типа и вызывает себя для classа (кроме String ) ,

    Вам понадобится параметр для размера отступа для использования с рекурсией.

    EDIT : вам также понадобится механизм для предотвращения переполнения стека для графиков циклических объектов. Я рекомендую установить ограничение на параметр отступа.

    Вот наивная реализация:

      private static void ListFields(Type type) { Console.WriteLine(type.Name); foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { Console.WriteLine(string.Format("{0} of type {1}", field.Name, field.FieldType.Name)); if (field.FieldType.IsClass) { ListFields(field.FieldType); } } } 

    Некоторые примечания:

    • Предотвратите переполнение стека. То есть, если a -> b и b-> a, тогда это взорвется. Вы можете разрешить это, разрешив только до определенного уровня
    • Строка является ссылочным типом, но многие люди ожидают, что она будет больше похожа на тип значения. Таким образом, вы можете не захотеть вызвать ListFields, если тип является строкой.