2010-07-25 17 views
5

Mam następujący problem. Mam tablicę bajtów, które chcę przekonwertować intro na tablicę typów pierwotnych. Ale nie znam tego typu. (Jest to podane jako tablica typów). W rezultacie potrzebuję szeregu obiektów.Konwertowanie tablicy bajtów na tablicę typów pierwotnych o nieznanym typie w języku C#

Oczywiście mogę użyć przełącznika typów (jest ich tylko ograniczona liczba), ale zastanawiam się, czy istnieje lepsze rozwiązanie.

Przykład:

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 

Czy istnieje algorytm na to czy powinienem użyć przełącznika

+0

Czy szukałeś wpisanych tablic lub po prostu obiektu []? –

+0

tylko obiekt [] chcę użyć tablicy obiektów do wywołania nieznanego konstruktora z klasą ConstructorInfo –

Odpowiedz

2

Ten kod wykorzystuje niebezpieczne, aby uzyskać wskaźnik do bufora tablicy bajtowej, ale nie powinno to stanowić problemu.

[Edit - zmieniony kod po komentarzu]

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

Wydaje się, że jest to marnotrawstwo, aby używać zarówno obiektu MemoryStream, jak i wskaźnika, gdy można po prostu mieć wskaźnik do 'byteData' i wykonać arytmetykę wskaźnika. –

+0

@Ben: Zgadzam się, to było z mojej strony leniwy i teraz go zmodyfikowałem. –

3

Oto moje pomysły:

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(); 

Zauważ, że wszystkie moje rozwiązania powyżej zakłada symetrię między byteData i types.

W przeciwnym razie trzeba przygotować symetryczną tablicę, która będzie zawierać indeks asymetrycznym tablicy:

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 
+0

Wygląda OK, ale musisz wiedzieć, jak dane zostały przekonwertowane na bajt []. Mogą pojawić się problemy z porządkiem bajtów. –

+1

To rozwiązanie (jak sądzę) wykonuje mapowanie 1 na 1 w tablicy bajtów. W tym przypadku chcę odwzorować n bajtów na 1 typ pierwotny (gdzie n jest rozmiarem typu prymitywnego w bajtach (4 dla int, 2 dla skrótu, 8 dla float, ...)). Lub czy klasa Convert używa tablicy jako wskaźnika? –

+0

@CommuSoft: Zaktualizowałem swój post – abatishchev

0

Można użyć refleksji do tworzenia tablic i wypełnić je. (Zauważ obsługi błędów z powodu błędnych danych dla 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); 
     } 
    } 
    } 
0

Można użyć 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)); 
      } 
     } 
    } 
} 

a następnie:

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

trochę brudne, ale działa ... sp służy do wskazania, gdzie odczytać z następnym w byteData, sprawdzanie typów można zrobić kilka w inny sposób, myślę ... ale to tylko pomysł. Więc proszę, nie -1 mnie, jeśli ci się nie podoba. =)

 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; 

      } 
     } 
Powiązane problemy