2009-05-08 10 views
4

Ich habe ein kleines Problem mit FieldOffset korrekt mit Arrays. Der folgende Code ist ein Beispiel, wo es nicht richtig für mich funktioniert:C# StructLayout/FieldOffset und Indizierung in Arrays

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

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

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

Wenn ich zum Beispiel das Array „Daten“ auf eine serialisierte Byte-Array namens setzt und dann versuchen, Daten wie Shorts abzurufen unter Verwendung " idx16 "Feld ist die Indizierung immer noch als Byte [] ausgerichtet. Das bedeutet, dass idx16 1 das zweite Byte in Daten holt, nicht das zweite 16-Bit-Wort (Byte 2 und 3). Wenn ich das Inverse mache, indexiere ich Kurzschlüsse anstelle von Bytes, was bedeutet, dass die Versatzausrichtung von den Quelldaten geerbt wird. Meine Frage, gibt es eine Möglichkeit, dies zu umgehen? Ich weiß, dass ich den Indexwert durch Multiplikation mit der Größe des Elements kompensieren kann, aber gibt es einen anderen Weg?

Here ist eine Antwort, die ich hier auf StackOverflow gefunden habe, aber als ich diesen Code ausprobierte, stellte sich heraus, dass er nicht richtig funktionierte. Versuchte es mit einem Unit-Test in VS mit dem folgenden Code ohne Erfolg:

[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); 
} 

Vielen Dank im Voraus!

+0

Re Ihren Kommentar; Natürlich ist es vielleicht besser, den Index durch die Größe zu teilen, als unsicheren Code ;-p –

Antwort

4

Das Problem ist (von dem, was ich sehen kann), dass Sie die Referenzen der Arrays unioned haben - so wird das Array, das zuletzt gesetzt wird, gewinnen. Sobald es ein Array gibt, verwendet es den Indexer (nicht Byte-Offset) - so spielt die Größe keine Rolle.

Die Art und Weise dieses „richtig“ (oder falsch, wie es der Fall sein kann) zu tun, wahrscheinlich mit unsicherem Code wäre - der Zeiger auf das Array nehmen - so etwas wie:

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 
     } 
    } 

Natürlich, ich bin mir nicht sicher, ob ich es empfehle - aber das scheint zu erreichen, was du willst?

Ich frage mich auch, ob Sie die struct komplett über Bord werfen können und verwenden nur unsicheren Zugang zu einem byte[] direkt:

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

Ja, das würde funktionieren und ich habe es in Betracht gezogen, aber wenn es einen Weg gibt, nicht in unsicheren Code zu springen, würde ich es bevorzugen. Aber Ihre Methode ist gut und wenn alles andere fehlschlägt, werde ich Ihre Empfehlung verwenden. – Burre

+0

@Burre Reinterpret Casting verwaltete Referenzen, wenn viel problematischer als unsicheren Code. Es gibt keinen Grund, unsicheren Code zu vermeiden, wenn Sie Low-Level-Sachen machen, aber zu vermeiden, das CLR-Typ-System völlig pervertiert zu haben, ist EVIL. Ich nehme an, dass Ihre Neuinterpretation zu undefiniertem Verhalten führt und in zukünftigen Versionen der CLR möglicherweise nicht mehr funktioniert. – CodesInChaos

+1

@CodeInChaos oder tatsächlich, in WinRT –

-2

Ihr FieldOffset definiert, wo jedes Ihrer Datenelemente im Innern der Struktur ist ..

Wenn Sie alle auf 0 setzen, sagen Sie dem Compiler, dass alle auf Position 0 stehen.

Die zweite Sache, die ich sehe, ist, dass Sie ein Array von Bytes, Kurzschlüssen und Ints erstellen.

siehe: 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

Overlaying sie an Position 0 ist beabsichtigt, um die drei Felder auf den gleichen Speicherbereich zeigen. Ich hatte erwartet, dass das Abrufen der Byte-Werte mit IDX16 die Bytes als kurze und nicht als Bytes ausrichten würde, aber das scheint nicht zu funktionieren und ich frage mich warum. – Burre