2010-07-25 21 views
5

J'ai le problème suivant. J'ai un tableau d'octets que je veux convertir intro un tableau de types primitifs. Mais je ne connais pas le type. (Ceci est donné comme un tableau de types). En conséquence, j'ai besoin d'un tableau d'objets.Conversion d'un tableau d'octets en un tableau de types primitifs de type inconnu en C#

Bien sûr, je pourrais utiliser un commutateur sur les types (il n'y en a qu'un nombre limité), mais je me demande s'il existe une meilleure solution pour cela.

Exemple:

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 

Y at-il un algorithme pour cela ou devrais-je utiliser un commutateur

+0

Avez-vous recherché des tableaux typés ou juste des objets []? –

+0

juste objet [] Je veux utiliser le tableau d'objets pour appeler un constructeur inconnu avec la classe ConstructorInfo –

Répondre

2

Ce code utilise dangereux pour obtenir un pointeur vers le tampon tableau d'octets, mais cela ne devrait pas être un problème.

[Edit - a changé après le code commentaire]

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

Cela ressemble à un gaspillage d'utiliser à la fois un MemoryStream et un pointeur, alors que vous pourriez juste avoir un pointeur sur 'byteData' et faire de l'arithmétique de pointeur. –

+0

@Ben: Je suis d'accord, c'était paresseux de ma part, et je l'ai modifié maintenant. –

3

Voici mes idées:

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

Notez que toutes mes solutions ci-dessus suppose la symétrie entre byteData et types.

Sinon, vous devez préparer un tableau symétrique qui contiendra un indice de tableau asymétrique:

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

Cela semble correct, mais vous avez besoin de savoir comment les données ont été converties _to_ octet []. Il pourrait y avoir des problèmes d'ordre d'octet. –

+1

Cette solution (je pense) fait un mappage 1-to-1 sur le byte-array. Dans ce cas je veux un mappage de n octets à 1 type primitif (où n est la taille du type primitve en octets (4 pour un int, 2 pour un short, 8 pour un float, ...)). Ou est-ce que la classe Convert utilise le tableau comme un pointeur? –

+0

@CommuSoft: J'ai mis à jour mon message – abatishchev

0

Vous pouvez utiliser la réflexion pour créer les tableaux et les remplir. (Notez que le gestionnaire d'erreurs en raison de données erronées pour 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

Vous pouvez utiliser 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)); 
      } 
     } 
    } 
} 

Et puis:

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

peu sale mais ça marche ... sp est utilisé pour indiquer où lire suivante byteData, la vérification des types peut être fait quelques-uns D'une autre façon je suppose ... mais c'est juste une idée. Donc s'il vous plaît ne me -1 si vous ne l'aimez pas. =)

 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; 

      } 
     }