C# Универсальный способ структурирования массивов структур

Я искал довольно долгое время, но я не смог найти, как в общем случае привести в порядок массив структур в C# во время выполнения.

Допустим, у меня есть

public enum FieldInfoType
{
    FitUInt32,
    FitStruct,
    FitUint32Array1D,
    FitUint32Array2DAs1D,
    FitStruct2DAs1D,
}

[AttributeUsage(AttributeTargets.Field)]
public class FieldInfo : Attribute
{
    private readonly String _description;
    private readonly FieldInfoType _type;
    private readonly int _dim1;
    private readonly int _dim2;

    public FieldInfo(String d, FieldInfoType t)
    {
        _description = d;
        _type = t;
    }

    public FieldInfo(String d, FieldInfoType t, int d1, int d2)
    {
        _description = d;
        _type = t;
        _dim1 = d1;
        _dim2 = d2;
    }

    public static void StringToStruct<T>(ref T myStruct, CalTextReader tr)
    {
        foreach (var field in myStruct.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            foreach (Attribute attr in GetCustomAttributes(field))
            {
                var info = attr as FieldInfo;
                if (info != null)
                {
                    FieldInfo fieldInfo = info;
                    var fieldVal = field.GetValue(myStruct);

                    switch (fieldInfo._type)
                    {
                        case FieldInfoType.FitStruct:
                            var structure = field.GetValue(myStruct);
                            StringToStruct(ref structure, tr);
                            field.SetValue(myStruct, structure);
                            break;
                        case FieldInfoType.FitUInt32:
                            field.SetValue(myStruct, tr.ReadUInt32());
                            break;
                        case FieldInfoType.FitUint32Array1D:
                            UInt32[] a = (UInt32[])fieldVal;
                            for (int i = 0; i < a.Length; i++)
                            {
                                a[i] = tr.ReadUInt32();
                            }
                            field.SetValue(myStruct, a);
                            break;
                        case FieldInfoType.FitUint32Array2DAs1D:
                            UInt32[] ui32Ary = (UInt32[])fieldVal;
                            int ui32AryIndex = 0;
                            for (int i = 0; i < fieldInfo._dim1; i++)
                            {
                                for (int j = 0; j < fieldInfo._dim2; j++)
                                {
                                    ui32Ary[ui32AryIndex++] = tr.ReadUInt32();
                                }
                            }
                            field.SetValue(myStruct, ui16Ary);
                            break;
                        case FieldInfoType.FitStruct2DAs1D:
                            throw new NotSupportedException();

                        default:
                            throw new Exception("Structure field " + field.Name + "must have a type");
                    }
                }
            }
        }
    }

    public static void StructToString<T>(T myStruct, TextWriter tw)
    {
        foreach (var field in myStruct.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            foreach (Attribute attr in GetCustomAttributes(field))
            {
                var fieldInfo = attr as FieldInfo;
                if (fieldInfo != null)
                {
                    var fieldVal = field.GetValue(myStruct);

                    switch (fieldInfo._type)
                    {
                        case FieldInfoType.FitStruct:
                            tw.WriteLine("# {0}", fieldInfo._description);
                            StructToString(field.GetValue(myStruct), tw);
                            break;
                        case FieldInfoType.FitUInt32:
                            tw.WriteLine("0x{0:x8} \t# {1} - {2}", fieldVal, field.Name, fieldInfo._description);
                            break;
                        case FieldInfoType.FitUint32Array1D:
                            UInt32[] d = (UInt32[])fieldVal;
                            for (int i = 0; i < d.Length; i++)
                            {
                                tw.WriteLine("0x{0:x8} \t# {1}[{2}]", d[i], field.Name, i);
                            }
                            break;
                        case FieldInfoType.FitUint32Array2DAs1D:
                            UInt32[] ui32Ary = (UInt32[]) fieldVal;
                            int ui16AryIndex = 0;
                            for (int i = 0; i < fieldInfo._dim1; i++)
                            {
                                for (int j = 0; j < fieldInfo._dim2; j++)
                                {
                                    tw.WriteLine("0x{0:x4} \t# {1}[{2},{3}]", ui32Ary[ui32AryIndex++], field.Name, i, j);
                                }
                            }
                            break;

                        case FieldInfoType.FitStruct2DAs1D:
                            throw new NotSupportedException();

                        default:
                            throw new Exception("Structure field " + field.Name + "must have a type");
                    }
                }
            }
        }
    }
}

public static class ArrayOp
{
    public static void Append<T>(ref T[] startingArray, T[] appendArray)
    {
        int startingArraySize = startingArray.Length;
        Array.Resize(ref startingArray, startingArraySize + appendArray.Length);
        Buffer.BlockCopy(appendArray, 0, startingArray, startingArraySize, appendArray.Length);
    }


    public static T[] Pack2DarrayInto1Darray<T>(T[,] input)
    {
        T[] result = new T[input.Length];
        int index = 0;
        for (int i = 0; i < input.GetLength(0); i++)
        {
            for (int j = 0; j < input.GetLength(1); j++)
            {
                result[index++] = input[i, j];
            }
        }
        return result;
    }

    public static T[,] Unpack1DarrayInto2Darray<T>(T[] input, int x, int y)
    {
        T[,] temp = new T[x, y];
        int index = 0;
        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                temp[i, j] = input[index++];
            }
        }
        return temp;
    }
}

Затем я могу объявить структуру следующим образом

private int cost Rows = 10;
private int cost Cols = 20;

[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public class MyStruct1
{
    [FieldInfo("My description of a UInt32 field", FieldInfoType.FitUInt32)]
    public UInt32 A;

    [FieldInfo("My description of a UInt32[] field", FieldInfoType.FitUint32Array1D)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = NumParallaxSpares)]
    public UInt32[] B;

    [FieldInfo("My description of a UInt32[,] field", FieldInfoType.FitUint32Array2DAs1D, Rows, Cols)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = (Rows*Cols))]
    private UInt32[] _C;
    public UInt32[,] C
    {
        get
        {
            return ArrayOp.Unpack1DarrayInto2Darray(_C, Rows, Cols);
        }
        set { _C = ArrayOp.Pack2DarrayInto1Darray(value); }
    }
}

Все выше работает как положено. Теперь то, что я хочу сделать, это привести в порядок массив структур, как

[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public class MyStruct2
{
    [FieldInfo("My description of a UInt32 field", FieldInfoType.FitUInt32)]
    public UInt32 A;

    [FieldInfo("My description of a UInt32 field", FieldInfoType.FitUInt32)]
    public UInt32 B;
}

[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public class MyStruct3
{        
    [FieldInfo("My description of a MyStruct2[,] field", FieldInfoType.FitStruct2DAs1D, Rows, Cols)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = (Rows*Cols))]
    private MyStruct2[] _C;
    public MyStruct2[,] C
    {
        get
        {
            return ArrayOp.Unpack1DarrayInto2Darray(_C, Rows, Cols);
        }
        set { _C = ArrayOp.Pack2DarrayInto1Darray(value); }
    }
}

Какой правильный общий способ справиться с этим в моих функциях StructToString() и StringToStruct()? В идеале это будет рекурсивный вызов одной и той же функции для каждого из элементов массива, но с атрибутом FieldInfoType.FitStruct.

0 ответов

Другие вопросы по тегам