2009-09-06 7 views
1

C'est ce que j'ai trouvé jusqu'à présent, mais cela ne semble pas très optimal, des idées sur de meilleures approches?Convertir un tableau de types de valeurs différents en un tableau d'octets

public void ToBytes(object[] data, byte[] buffer) 
{ 
    byte[] obytes; 
    int offset = 0; 

    foreach (object obj in data) 
    { 
     if (obj is string) 
      obytes = System.Text.Encoding.UTF8.GetBytes(((string)obj)); 
     else if (obj is bool) 
      obytes = BitConverter.GetBytes((bool)obj); 
     else if (obj is char) 
      obytes = BitConverter.GetBytes((char)obj); 
     // And so on for each valuetype 

     Buffer.BlockCopy(obytes, 0, buffer, offset, obytes.Length); 
     offset += obytes.Length; 
    } 
} 

Répondre

4

Eh bien, vous pourriez avoir une carte comme celle-ci:

private static readonlyDictionary<Type, Func<object, byte[]>> Converters = 
    new Dictionary<Type, Func<object, byte[]>>() 
{ 
    { typeof(string), o => Encoding.UTF8.GetBytes((string) o) }, 
    { typeof(bool), o => BitConverter.GetBytes((bool) o) }, 
    { typeof(char), o => BitConverter.GetBytes((char) o) }, 
    ... 
}; 

public static void ToBytes(object[] data, byte[] buffer) 
{ 
    int offset = 0; 

    foreach (object obj in data) 
    { 
     if (obj == null) 
     { 
      // Or do whatever you want 
      throw new ArgumentException("Unable to convert null values"); 
     } 
     Func<object, byte[]> converter; 
     if (!Converters.TryGetValue(obj.GetType(), out converter)) 
     { 
      throw new ArgumentException("No converter for " + obj.GetType()); 
     } 

     byte[] obytes = converter(obj); 
     Buffer.BlockCopy(obytes, 0, buffer, offset, obytes.Length); 
     offset += obytes.Length; 
    } 
} 

vous spécifiez toujours le convertisseur pour chaque type, mais il est beaucoup plus compact que le si/forme d'autre.

Il existe plusieurs autres façons de construire le dictionnaire, btw. Vous pouvez le faire comme ceci:

private static readonly Dictionary<Type, Func<object, byte[]>> Converters = 
     new Dictionary<Type, Func<object, byte[]>>(); 

static WhateverYourTypeIsCalled() 
{ 
    AddConverter<string>(Encoding.UTF8.GetBytes); 
    AddConverter<bool>(BitConverter.GetBytes); 
    AddConverter<char>(BitConverter.GetBytes); 
} 

static void AddConverter<T>(Func<T, byte[]> converter) 
{ 
    Converters.Add(typeof(T), x => converter((T) x)); 
} 

Je vois une autre réponse a suggéré sérialisation binaire. Personnellement, je ne suis pas intéressé par les schémas de sérialisation "opaques" comme ça. J'aime savoir exactement ce qui se passera dans les données de telle sorte que je puisse le porter sur d'autres plateformes.

Je voudrais souligner, cependant, que votre schéma actuel ne donne aucune sorte de délimiteur - si vous avez deux chaînes, vous n'auriez aucune idée où l'un s'est arrêté et l'autre a commencé, par exemple. Vous ne stockez pas non plus les informations de type - cela peut être correct, mais ce n'est peut-être pas le cas. La question de la longueur variable est généralement plus importante. Vous pouvez envisager d'utiliser un schéma de longueur-préfixe, comme celui de BinaryWriter. En effet, BinaryWriter pourrait bien être une solution plus simple en général. Vous voudrez probablement toujours avoir une carte des délégués, mais faites-leur des actions en prenant un BinaryWriter et une valeur. Vous pourriez ensuite construire la carte par réflexion, ou simplement une liste d'appels codés en dur.

alors vous simplement initialiser un BinaryWriter enroulant un MemoryStream, écrire chaque valeur de manière appropriée, puis appelez ToArray sur le MemoryStream pour obtenir les résultats.

+0

Plus important encore, le schéma actuel ne fournit aucun type d'information de type. Vous n'aurez aucune idée si ce que vous lisez est un 'integer' ou un' float', par exemple. –

+0

@Mehrdad: Cela peut être un problème, mais ce n'est peut-être pas le cas. L'information de type * peut être implicite - par ex. les objets peuvent être les valeurs des champs d'un type, où le type global est connu à l'avance. –

+0

Merci pour vos réponses. Je connais le problème de la longueur variable, mais j'ai essayé de limiter la question. La solution que j'ai utilisée pour cela est de stocker la longueur de chaque objet en tant que shorts dans un tableau d'octets séparé qui après la conversion copié dans le tampon. Les informations de type sont connues à la fois par le client et le serveur, ce qui ne pose aucun problème. Votre solution semble intéressante car le serveur doit pouvoir communiquer avec un client .NET et un client Java. – remdao

2

Probablement, vous devriez envisager d'utiliser à la place BinaryFormatter:

var formatter = new BinaryFormatter(); 
var stream = new MemoryStream(); 
formatter.Serialize(stream, obj); 
byte[] result = stream.ToArray(); 

A côté de cela, il y a quelques très bons cadres de sérialisation comme Google Protocol Buffers si vous voulez éviter de réinventer la roue.

+0

Merci. J'aurais utilisé BinaryFormatter s'il y avait un moyen de désérialiser les données en Java. Je vais regarder dans les tampons de protocole Google. Savez-vous s'il existe une version Java? – remdao

+1

remdao: Oui, il existe une version Java: http://code.google.com/p/protobuf/ Si vous voulez avoir un code de sérialisation multi-plateforme, il existe une autre implémentation C# par Jon qui fonctionne mieux pour ce scénario: http://code.google.com/p/protobuf-csharp-port –

0

Vous pouvez utiliser un StreamWriter pour écrire dans un flux de mémoire et utiliser son tampon:

{ 
       byte[] result; 
      using (MemoryStream stream = new MemoryStream()) 
      { 
       StreamWriter writer = new StreamWriter(stream); 
       writer.WriteLine("test"); 
       writer.WriteLine(12); 
       writer.WriteLine(true); 

       writer.Flush(); 

       result = stream.GetBuffer(); 
      } 

      using(MemoryStream stream=new MemoryStream(result)) 
      { 
       StreamReader reader = new StreamReader(stream); 
       while(! reader.EndOfStream) 
       Console.WriteLine(reader.ReadLine()); 
       } 
      } 
Questions connexes