2010-07-25 22 views
5

Tengo el siguiente problema. Tengo una matriz de bytes que quiero convertir una matriz de tipos primitivos. Pero no sé el tipo. (Esto se da como una matriz de tipos). Como resultado, necesito una variedad de objetos.Convirtiendo una matriz de bytes a una matriz de tipos primitivos con tipo desconocido en C#

Por supuesto que podría usar un interruptor en los tipos (hay solo un número limitado de ellos), pero me pregunto si hay una mejor solución para eso.

Ejemplo:

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 

¿Existe un algoritmo para esto o debo utilizar un interruptor

+0

¿Buscó matrices tipadas o simplemente object []? –

+0

just object [] quiero usar la matriz de objetos para invocar un Constructor desconocido con la clase ConstructorInfo –

Respuesta

2

Este código utiliza inseguro para obtener una puntero al búfer de matriz de bytes, pero eso no debería ser un problema.

[Editar - código después cambió comentario]

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

Parece un desperdicio utilizar tanto un MemoryStream como un puntero, cuando podría tener un puntero a 'byteData' y hacer aritmética de puntero. –

+0

@Ben: estoy de acuerdo, era flojo de mi parte, y lo he modificado ahora. –

3

Aquí está mi ideas:

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

Tenga en cuenta, que todos mis soluciones anterior supone la simetría entrebyteData y types.

De lo contrario usted tiene que preparar una matriz simétrica que contendrá un índice de la matriz asimétrica:

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

Se ve bien pero no necesita saber cómo se convirtieron los datos _to_ byte []. Podría haber problemas de orden de bytes. –

+1

Esta solución (creo) realiza una asignación de 1 a 1 en la matriz de bytes. En este caso, quiero una asignación de n bytes a 1 tipo primitivo (donde n es el tamaño del tipo de primitve en bytes (4 para un int, 2 para un corto, 8 para un float, ...)). ¿O la clase Convert usa la matriz como un puntero? –

+0

@CommuSoft: actualicé mi publicación – abatishchev

0

Puede utilizar la reflexión para crear las matrices y llenarlos. (Nótese el gestor de errores debido a datos incorrectos para 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

Se puede usar un 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)); 
      } 
     } 
    } 
} 

Y luego:

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

poco sucio, pero funciona ... sp se utiliza para señalar dónde leer a partir del próximo en byteData, la comprobación de tipos se puede hacer alguna de otra manera, supongo ... pero esto es solo una idea. Así que, por favor, no, mí, si no te gusta. =)

 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; 

      } 
     } 
Cuestiones relacionadas