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.