Рекурсивно перебирать структуры, добавляя их значения в список

У меня есть структуры, которые могут содержать другие структуры и класс с двумя свойствами, одним для имени Поля и одним для значения поля.

Пример структур:

    public struct MyStruct
    {
        public string Name;
        public ushort Code;
        public Details Info;
    }

    public struct Details
    {
        public string Architecture;
        public string Characteristics;
        public uint Size;
    }

Мой класс:

public class Item
{
    public string Name { get; set; }
    public object Value { get; set; }
    public Item(string name, object value)
    {
        Name = name;
        Value = value;
    }
}

Теперь мне нужна функция, входной параметр которой является структурой (которая может содержать другие структуры) и возвращает список элементов.
У меня есть полностью рабочая функция для структур без других структур в нем:

    private static List<Item> structToList<T>(T structure)
    {
        List<Item> items = new List<Item>();
        foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
            items.Add(new Item(field.Name, field.GetValue(structure)));
        return items;
    }

Я уверен, что это можно решить рекурсивно. Моей первой мыслью было проверить, является ли поле структурой или значением. Если это структура, снова вызовите функцию, если это значение, добавьте его в список. Кроме того, я должен объявить экземпляр моего списка вне функции, не так ли? Вот псевдо-код, который я придумал, поэтому я не смог проверить, является ли FieldInfo структурой и дать функции свой собственный универсальный.

    List<Item> items = new List<Item>();
    private static List<Item> structToList<T>(T structure, List<Item> items)
    {

        foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            //if(isStructure(field)
            //      structToList<?>(field, items);
            //else
                    items.Add(new Item(field.Name, field.GetValue(structure)));
        }  
        return items;
    }

РЕДАКТИРОВАТЬ:

Разграничение дел работает и сейчас. Для предложения else я пошел с этим ответом, где тип известен во время выполнения, но я получаю исключение null reference. Также поле.GetType () не дает мне тип структуры.

    private List<Item> structToList<T>(T structure, uint offset)
    {

        foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            if (IsSimple(field.FieldType))
                itemList.Add(new Item(field.Name, field.GetValue(structure)));
            else
            {
                // doesn't work
                Type t = field.GetType();
                MethodInfo method = GetType().GetMethod("structToList")
                         .MakeGenericMethod(new Type[] { t });  // null reference exception
                method.Invoke(this, new object[] { field, 0 });
            }
        }
        return itemList;
    }

    private static bool IsSimple(Type type)
    {
        return type.IsPrimitive
          || type.IsEnum
          || type.Equals(typeof(string))
          || type.Equals(typeof(decimal));
    }

EDIT 2:
Я придумал два решения, которые соответствуют моим потребностям.

2 ответа

  1. Есть области, которые вам нужно решить (как вы упомянули):

    1. Во-первых, вы должны определить, когда тип поля является structили «простое» значение. Для этого предлагаю вам прочитать ответ на этот вопрос.

    2. Теперь, чтобы сделать это рекурсивно, я бы отбросил общий параметр и просто сделал typof()бы structвсе, что передается в метод. Это сделает вызов метода с внутренним structs намного проще, так как вам не нужно будет делать некоторые отражения «тяжелый подъем» просто установить structToListобщий метод с определенным типом.

  2. Я придумал два правильных решения, которые должны решить мою проблему.
    Оба способа воздерживаются от использования дженериков, потому что мы предполагаем, что мы не знаем тип структуры в структуре во время компиляции.

    1) Сначала я использовал эту функцию, упомянутую в этом ответе, чтобы дифференцировать, если это примитивный тип данных, который был расширен некоторыми специальными случаями, например строкой, которая в основном является массивом символов. После определения структуры следующего поля можно снова рекурсивно вызвать функцию или добавить ее в список.

        private List<Item> _itemList = new List<Item>();
    
        private List<Item> structToList(object structure)
        {
            foreach (var field in structure.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
            {
                if (!IsSimple(field.FieldType))
                    structToList(field.GetValue(structure));
                else
                    _itemList.Add(new Item(field.Name, field.GetValue(structure)));
            }
            return _itemList;
        }
    
        private static bool IsSimple(Type type)
        {
            return type.IsPrimitive
              || type.IsEnum
              || type.Equals(typeof(string))
              || type.Equals(typeof(decimal));
        }
    

    2) второе решение использует тип.IsNested свойство для достижения того же.

        private List<Item> _itemList = new List<Item>();
    
        private List<Item> structToList1(object structure)
        {
            foreach (var field in structure.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
            {
                if (field.FieldType.IsNested)
                    structToList(field.GetValue(structure));
                else
                    _itemList.Add(new Item(field.Name, field.GetValue(structure)));
            }
            return _itemList;
        }