Преобразование байтового массива в массив примитивных типов с неизвестным типом в C # - PullRequest
5 голосов
/ 25 июля 2010

У меня следующая проблема. У меня есть массив байтов, которые я хочу преобразовать в массив примитивных типов. Но я не знаю тип. (Это дано как массив типов). В результате мне нужен массив объектов.

Конечно, я мог бы использовать переключатель типов (их ограниченное количество), но мне интересно, есть ли лучшее решение для этого.

Пример:

byte[] byteData = new byte[] {0xa0,0x14,0x72,0xbf,0x72,0x3c,0x21}
Type[] types = new Type[] {typeof(int),typeof(short),typeof(sbyte)};

//some algorithm

object[] primitiveData = {...};
//this array contains an the following elements
//an int converted from 0xa0,0x14,0x72,0xbf
//a short converted from 0x72, 0x3c
//a sbyte converted from 0x21

Есть ли алгоритм для этого или я должен использовать переключатель

Ответы [ 5 ]

3 голосов
/ 25 июля 2010

Вот мои идеи:

object[] primitiveData = new object[byteData.Lenght];
for (int i = 0; i < bytesData.Lenght; i++)
{
     primitiveData[i] = Converter.ChangeType(bytesData[i], types[i]);
}
object[] primitiveData = new object[bytDate.Lenght];
for (int i = 0; i < bytesDate.Lenght; i++)
{
     Type t = types[i];
     if (t == typeof(int))
     {
          primitiveData[i] = Convert.ToInt32(bytesDate[i]);
     }
     else if (t == typeof(short))
     {
          primitiveData[i] = Convert.ToInt16(bytesDate[i]);
     }
     ..
}
var dic = new Dictionary<Type, Func<byte, object>>
{
    { typeof(int), b => Convert.ToInt32(b) },
    { typeof(short), b => Convert.ToInt16(b) },
    ...
};

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

List<object> list = new List<object>(primitiveData.Length);
for (int i = 0; i < primitiveData.Length; i++)
{
     Byte b = byteData[i];
     Type t = types[i];
     Func<byte, object> func = dic[t];
     list.Add(func(b));
}
object[] primitiveData = list.ToArray();
byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
// delegates to converters instead of just appropriate types
Func<byte, object>[] funcs = new Func<byte, object>[]
{
     b => Convert.ToInt32(b),
     b => Convert.ToInt16(b),
     b => Convert.ToSByte(b)
};

List<object> list = new List<object>(primitiveData.Length);
for (int i = 0; i < primitiveData.Length; i++)
{
     Byte b = byteData[i];
     Func<byte, object> func = funcs[i];
     list.Add(func(b));
}
object[] primitiveData = list.ToArray();

Обратите внимание, что все мои решения, приведенные выше, предполагают симметрию между byteData и types.

В противном случае вы должны подготовить симметричный массив, который будет содержать индекс асимметричного массива:

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) }; // asymmetric 
int[] indexes = new int[] { 0, 0, 0, 0, 1, 2 }; // symmetric 
2 голосов
/ 25 июля 2010

Этот код использует unsafe для получения указателя на буфер байтового массива, но это не должно быть проблемой.

[Редактировать - изменил код после комментария]

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

object[] result = new object[types.Length];
unsafe
{
    fixed (byte* p = byteData)
    {
        var localPtr = p;
        for (int i = 0; i < types.Length; i++)
        {
            result[i] = Marshal.PtrToStructure((IntPtr)localPtr, types[i]);
            localPtr += Marshal.SizeOf(types[i]);
        }
    }
}
1 голос
/ 25 июля 2010

Вы можете использовать BinaryReader :

public static IEnumerable<object> ConvertToObjects(byte[] byteData, Type[] types)
{
    using (var stream = new MemoryStream(byteData))
    using (var reader = new BinaryReader(stream))
    {
        foreach (var type in types)
        {
            if (type == typeof(short))
            {
                yield return reader.ReadInt16();
            }
            else if (type == typeof(int))
            {
                yield return reader.ReadInt32();
            }
            else if (type == typeof(sbyte))
            {
                yield return reader.ReadSByte();
            }
            // ... other types
            else
            {
                throw new NotSupportedException(string.Format("{0} is not supported", type));
            }
        }
    }
}

А затем:

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };
object[] result = ConvertToObjects(byteData, types).ToArray();
0 голосов
/ 25 июля 2010

Немного грязно, но это работает ... sp используется для указания того, где читать дальше в byteData, проверка типов может быть выполнена другим способом, я думаю ... но это всего лишь идея.Поэтому, пожалуйста, не надо меня, если вам это не нравится.=)

        byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
        Type[] types = new Type[] {typeof(int),typeof(short),typeof(sbyte)};

        object[] primitiveData = new object[types.Length];
        int sp = 0;

        for(int i=0; i<types.Length; i++)
        {

            string s = types[i].FullName;
            switch(types[i].FullName)
            {
                case "System.Int32":{
                    primitiveData[i] = BitConverter.ToInt32(byteData, sp);
                    sp += 4;
                }break;
                case "System.Int16":
                    {
                    primitiveData[i] = BitConverter.ToInt16(byteData, sp);
                    sp += 2;
                }break;
                case "System.SByte":
                    {
                    primitiveData[i] = (sbyte)byteData[sp];
                    sp += 1;
                }break;

            }
        }
0 голосов
/ 25 июля 2010

Вы можете использовать отражение, чтобы создать массивы и заполнить их.(Обратите внимание на обработчик ошибок из-за неправильных данных для SByte):

  [TestMethod]
  public void MyTestMethod() {
     byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
     Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

     List<Array> result = new List<Array>();

     foreach (var type in types) {
        Type arrayType = type.MakeArrayType();
        ConstructorInfo ctor = arrayType.GetConstructor(new Type[] { typeof(int) });
        Array array = (Array)ctor.Invoke(new object[] { byteData.Length });

        for (int i = 0; i < byteData.Length; i++) {
           byte b = byteData[i];
           try {
              array.SetValue(Convert.ChangeType(b, type), i);
           } catch {
              Console.WriteLine("Error with type {0} and value {1}", type, b);
           }
        }

        result.Add(array);
     }

     // -------------------
     // show result
     foreach (var array in result) {
        Console.WriteLine(array.GetType());
        foreach (var item in array) {
           Console.WriteLine("   {0}", item);
        }
     }
  }
...