递归迭代结构将其值添加到列表中

时间:2016-12-31 16:27:24

标签: c# .net recursion struct

我有可以包含其他结构的结构和一个具有两个属性的类,一个用于字段名称,另一个用于字段值。

示例结构:

    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;
    }
}

现在我需要一个函数,其中输入参数是一个结构(可以包含其他结构)并返回一个List的Item。 对于没有其他结构的结构,我有一个完全有效的函数:

    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;
    }

我很确定这可以递归解决。我的第一个想法是检查字段是结构还是值。如果它是一个结构,请再次调用该函数,如果它是一个值,则将其添加到列表中。此外,我必须在函数之外声明我的List的实例,避难所不是吗?这是我提出的伪代码,所以我无法检查FieldInfo是否是一个结构,并为函数提供了正确的Generic。

    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子句,我使用this answer,其中类型在执行时已知,但我得到空引用异常。另外field.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));
    }

编辑2: 我想出了符合我需要的two solutions

2 个答案:

答案 0 :(得分:0)

您需要解决的问题(如您所述):

  1. 首先,您需要检测字段类型何时为struct或&#34;简单&#34;值。为此,我建议您阅读this问题的答案。

  2. 现在,为了递归地执行此操作,我将删除泛型参数,并在传递给方法的任何typof()上执行struct。这将使调用内部struct的方法变得更加容易,因为您不需要做一些反思&#34;繁重的提升&#34;只是设置具有特定类型的structToList泛型方法。

答案 1 :(得分:0)

我提出了两个适当的解决方案,我应该解决我的问题。 两种方式都避免使用泛型,因为我们假设在编译时我们不知道结构中结构的类型。

1)首先,我使用了in this answer这个函数来区分它是primitive数据类型,它是由某些特殊情况扩展的,例如字符串,基本上是一个字符数组。在能够确定下一个字段是否为结构之后,您可以再次递归调用函数或将其添加到列表中。

    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)第二个解决方案使用Type.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;
    }