2011-12-15 2 views
2

J'ai un System.Array de types struct de valeur, quelque chose comme ceci:Comment convertir un tableau de valeur struct en octets?

public value struct Position 
{ 
    int timestamp; 
    float x; 
    float y; 
} 

Position[] positions = new Position[1000 * 1000]; 

Après avoir initialiser le tableau avec des valeurs, comment puis-je obtenir un byte[] de son contenu, sans sérialisation un élément à la fois?

En C++/CLI, j'utiliserais un pin_ptr pour obtenir le pointeur sur le contenu du tableau et je copierais les données à partir de là. Puis-je faire quelque chose comme ça en C#?

EDIT: J'ai besoin d'écrire les données brutes sur le disque, comme s'il s'agissait d'une structure C, sans aucun type de sérialisation. J'ai marqué cette question en tant que C# pour une exposition plus large, mais en fait j'essaie de sérialiser les données de IronPython, donc cela signifie que je ne peux pas utiliser la fonctionnalité unsafe C#.

+1

Le 'Convert.GetBytes()' ne fonctionne-t-il pas? – Default

+0

Dupliquer: http://stackoverflow.com/questions/1068541/how-to-convert-a-value-type-to-byte-in-c – Kugel

+0

@Default: si vous faites référence à 'BitConverter.GetBytes', non ça ne marche pas. Il renvoie un 'byte []' avec un seul octet à l'intérieur – Meh

Répondre

1

est ici une méthode qui ne nécessite pas de code dangereux :

[Mis à jour pour supprimer la boucle for et effectuer la copie en un seul passage]

private static byte[] StructureToByteArray(Position[] posArray) 
{ 
    if (posArray == null || posArray.Length == 0) 
    { 
     return new byte[0]; 
    } 

    var lenOfObject = Marshal.SizeOf(typeof(Position)); 
    var len = lenOfObject * posArray.Length; 
    var arr = new byte[len]; 

    var handle = GCHandle.Alloc(posArray, GCHandleType.Pinned); 
    try 
    { 
     var ptr = handle.AddrOfPinnedObject(); 
     Marshal.Copy(ptr, arr, 0, len); 
    } 
    finally 
    { 
     handle.Free(); 
    } 

    return arr; 
} 
+0

Même question que sur la réponse dangereuse: Serait-il une bonne idée d'appliquer l'attribut 'StructLayout' avec un' LayoutKind' de 'Sequential' ou' Explicit' à la structure 'Position' si on utilise cette approche, ou est-ce pas important? –

+0

Ce serait une bonne idée de spécifier 'LayoutKind.Sequential'. Puisque les structures sont si souvent utilisées pour l'interopérabilité, c'est la valeur par défaut pour les compilateurs C#/VB.NET (et probablement d'autres), mais il n'y a aucune garantie que cela reste vrai. –

+0

Cela passe par chaque élément. Je veux éviter cela. – Meh

2

Peut-être que cela vous aidera:

[Serializable()] 
    public struct Position 
    { 
     int timestamp; 
     float x; 
     float y; 
    } 


    static void Main(string[] args) 
    { 
     var positions = new Position[1000 * 1000]; 
     GetBytes(positions); 
    } 
    private static byte[] GetBytes(object obj) 
    { 
     using (var memoryStream = new System.IO.MemoryStream()) 
     { 
      var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
      binaryFormatter.Serialize(memoryStream, obj); 
      return memoryStream.ToArray(); 
     } 
    } 
+0

Je ne pense pas que les formateurs 'Serialization' retournent les données brutes. – Meh

+0

Cette approche donne un tableau d'octets, et il nécessite très peu d'effort de codage, donc je pense que c'est une réponse parfaitement valide. Certes, le tableau d'octets résultant ne sera pas aussi petit que possible, car il contient certaines informations de type utilisées pour la désérialisation. C'est à l'individu de décider si cela convient à son besoin. –

+0

Je pense simplement que l'utilisation d'un paquet standard i C# est préférable à l'utilisation de code dangereux. Je pense également qu'il est vrai que lors de la sérialisation de l'objet, il doit enregistrer les informations de type. Donc le tableau d'octets est peut-être plus grand mais la solution standard est je pense préférable – Arion

1

Je crois que c'est l'équivalent du C++/CLI pin_ptr en utilisant le code dangereux C#:

public static unsafe byte[] GetBytes(Position[] positions) 
    { 
     byte[] result = new byte[positions.Length * Marshal.SizeOf(typeof(Position))]; 
     fixed (Position* ptr = &positions[0]) 
     { 
      Marshal.Copy((IntPtr)ptr, result, 0, result.Length); 
     } 
     return result; 
    } 
+0

Est-il nécessaire d'appliquer l'attribut StructLayout avec un LayoutKind de Sequential ou Explicit à la structure Position afin d'utiliser de manière fiable cette approche? Je me demandais juste. –

+0

Merci, mais je ne peux pas utiliser dangereux. J'ai mis à jour la description avec pourquoi. – Meh

+0

@Dr. Wily's Apprentice, je recommanderais certainement d'utiliser la mise en page explicite si vous essayez d'interopérer avec du code non géré qui attend une mise en page binaire spécifique.Je ne suis pas sûr de la force des garanties pour le positionnement automatique. –

Questions connexes