2009-05-08 7 views
4

J'ai un peu de difficulté à utiliser correctement FieldOffset avec les tableaux. Le code ci-dessous est un exemple où il ne fonctionne pas correctement pour moi:C# StructLayout/FieldOffset et l'indexation dans les tableaux

[StructLayout(LayoutKind.Explicit)] 
public struct IndexStruct { 
    [FieldOffset(0)] 
    public byte[] data; 

    [FieldOffset(0)] 
    public short[] idx16; 

    [FieldOffset(0)] 
    public int[] idx32; 
} 

Si je par exemple définit le tableau nommé « données » à un tableau d'octets sérialisés et puis essayer de récupérer des données sous forme de courts métrages à l'aide de la " champ idx16 "l'indexation est toujours alignée comme un octet []. Ce qui signifie que idx16 1 récupère le deuxième octet dans les données, pas le second mot de 16 bits (octets 2 et 3). Si je fais l'inverse, j'indexe les shorts au lieu des octets, ce qui signifie que l'alignement du décalage est hérité des données sources. Ma question, y a-t-il un moyen de contourner cela? Je sais que je peux compenser la valeur de l'indice en multipliant par la taille de l'élément, mais y a-t-il un autre moyen?

Here est une réponse que j'ai trouvé ici sur StackOverflow, mais en essayant ce code, il s'est avéré que cela ne fonctionnait pas correctement. Essayé à l'aide d'un test unitaire dans VS avec le code suivant sans succès:

[TestMethod()] 
public void SumTest() { 
    float[] fArr = {2.0f, 0.5f, 0.0f, 1.0f}; 
    MemoryStream ms = new MemoryStream(); 
    for (int i = 0; i < fArr.Length; i++) { 
     ms.Write(BitConverter.GetBytes(fArr[i]), 0, sizeof(float)); 
    } 
    byte[] buff = ms.ToArray(); 
    double expected = 3.5f; 
    double actual = Sum(buff); 
    Assert.AreEqual(expected, actual); 
} 

Merci beaucoup à l'avance!

+0

Re votre commentaire; bien sûr, diviser l'index par la taille peut être préférable à un code dangereux ;-p –

Répondre

4

Le problème est (d'après ce que je peux voir) que vous avez uni les références des tableaux - donc quel tableau sera fixé en dernier va gagner. Une fois qu'il y a un tableau, il utilise l'indexeur (pas le décalage d'octet) - donc la taille n'a pas d'importance.

La façon de le faire « correctement » (ou mal, selon le cas) serait probablement avec le code dangereux - prendre le pointeur sur le tableau - quelque chose comme:

IndexStruct s = new IndexStruct(); 
    s.data = new byte[] { 1, 0, 0, 0, 1, 1 }; 

    unsafe 
    { 
     fixed (short* data = s.idx16) 
     { 
      Console.WriteLine(data[0]); // should be 1 (little-endian) 
      Console.WriteLine(data[1]); // should be 0 
      Console.WriteLine(data[2]); // should be 257 
     } 
    } 

Bien sûr, je Je ne suis pas sûr que je le recommande - mais cela semble atteindre ce que vous voulez?

Je me demande aussi si vous pouvez laisser tomber le struct complètement et il suffit d'utiliser un accès dangereux à un byte[] directement:

byte[] raw = new byte[] { 1, 0, 0, 0, 1, 1 }; 
    unsafe 
    { 
     fixed (byte* addr = raw) 
     { 
      short* s = (short*)addr; 
      Console.WriteLine(s[0]); // should be 1 
      Console.WriteLine(s[1]); // should be 0 
      Console.WriteLine(s[2]); // should be 257 
     } 
    } 
+0

Oui cela fonctionnerait et j'ai envisagé de l'utiliser, mais s'il y a un moyen d'éviter de sauter dans un code dangereux, je le préférerais. Mais votre méthode est bonne et si tout le reste échoue, je vais utiliser votre recommandation. – Burre

+0

@Burre Réinterpréter la diffusion des références managées si le code n'est pas sûr. Il n'y a aucune raison d'éviter le code dangereux si vous faites des choses de bas niveau, mais évitez de pervertir complètement le système de type CLR est EVIL. Je suppose que votre distribution de réinterprétation conduit à un comportement indéfini et pourrait casser dans les futures versions du CLR. – CodesInChaos

+1

@CodeInChaos ou, en effet, dans WinRT –

-2

Votre FieldOffset définit où chacun de vos éléments de données sont à l'intérieur du struct ..

En les mettant tous à 0, vous dites au compilateur qu'ils sont tous en position 0.

La deuxième chose que je vois est que vous créez un tableau d'octets, de shorts et d'ints.

voir: MSDN StructLayoutAttribute

[StructLayout(LayoutKind.Explicit)] 
public struct IndexStruct { 
     [FieldOffset(0)] 
     public byte[16] data; 

     [FieldOffset(16)] 
     public short idx16; 

     [FieldOffset(18)] 
     public int idx32; 
} 
+0

Les superposer à la position 0 est intentionnel afin que les trois champs pointent vers la même zone de mémoire. Je m'attendais à ce que récupérer les valeurs de byte avec idx16 alignerait les octets comme des shorts et pas comme des octets, mais cela ne semble pas fonctionner et je me demande pourquoi. – Burre