Domanda

Recentemente ho incontrato una situazione in cui ho bisogno di creare un metodo generico per leggere un tipo di dati da un array di byte.

Ho creato la seguente classe:


public class DataStream
{
    public int Offset { get; set; }

    public byte[] Data { get; set; }

    public T Read<T>() where T : struct
    {
        unsafe
        {
            int dataLen = Marshal.SizeOf( typeof( T ) );
            IntPtr dataBlock = Marshal.AllocHGlobal( dataLen );


            Marshal.Copy( Data, Offset, dataBlock, dataLen );


            T type = *( ( T* )dataBlock.ToPointer() );

            Marshal.FreeHGlobal( dataBlock );

            Offset += dataLen;

            return type;
        }
    }
}

Ora, problemi di de-allocazione a parte, questo codice non compila con questo messaggio:

Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')

Il che, sembra strano, perché si dovrebbe essere in grado di fare le operazioni di cui sopra sulla base del vincolo where T : struct sul metodo.

Se questo codice è terribilmente errato, c'è un modo semplice per prendere una serie di byte e li getteranno in un tipo 'T'?

Grazie!

È stato utile?

Soluzione

Invece di cercare di farlo tramite la manipolazione dei puntatori, si dovrebbe passare il codice per utilizzare Mashal.PtrToStructure . Questo metodo è stato appositamente progettato per questo scenario.

Altri suggerimenti

Dal risposta è già stata data, lasciatemi spiegare perché il vostro codice originale non ha funzionato per voi:

  

Il che, sembra strano, perché si dovrebbe essere in grado di fare le operazioni di cui sopra in base alla quale T:. Vincolo struct sul metodo

Non proprio. Si possono avere i puntatori prime per tipi non gestiti . Questo è definito come segue nelle specifiche C # lingua (18,2):

  

A differenza di riferimenti (valori di tipi di riferimento), i puntatori non sono monitorati dal garbage collector - il garbage collector non è a conoscenza dei puntatori e dei dati a cui puntano. Per questo motivo un puntatore non è consentito per puntare a un riferimento o ad una struct che contiene riferimenti, e il tipo referente di un puntatore deve essere un non gestito di tipo .   Un non gestito di tipo è qualsiasi tipo che non è un riferimento di tipo e non contiene i campi di riferimento di tipo a qualsiasi livello di nidificazione. In altre parole, un non gestito di tipo è uno dei seguenti:

     
      
  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal o bool.
  •   
  • Qualsiasi enum di tipo .
  •   
  • Qualsiasi puntatore di tipo .
  •   
  • Qualsiasi definita dall'utente struct di tipo che contiene i campi di non gestiti tipi solo.
  •   

Quindi ci sono un bel paio di restrizioni, e per un metodo generico, T:struct possono essere o non conformi a loro per un particolare esemplificazione, in modo da costruire come T* è illegale. Sarebbe bello avere uno speciale vincolo di parametro di tipo generico per coprire i tipi non gestiti, ma così com'è, non ce n'è uno nel CLR.

A un certo punto ho scritto questo articolo che spiega come fare esattamente questo , ma molte volte più veloce rispetto alla Marshal.PtrToStructure. L'esempio di codice utilizza la generazione di codice dinamico per copiare il tipo generico T da / per flusso di bit.

Supponendo che:

  • Una struttura sequenziale o esplicita (altrimenti pessima idea)
  • formati di dati corretto (si dovrebbe pre-check e buttare)

unsafe TStruct BytesToStructure<TStruct>(byte[] data) where TStruct : struct
{
    fixed (byte* dataPtr = data)
        return (TStruct)Marshal.PtrToStructure(new IntPtr(dataPtr), typeof(TStruct));
}

unsafe byte[] StructureToBytes<TStruct>(TStruct st) where TStruct : struct
{
    var bytes = new byte[Marshal.SizeOf(st)];
    fixed (byte* ptr = bytes) Marshal.StructureToPtr(st, new IntPtr(ptr), true);
    return bytes;
}

Ho scritto questo un po 'indietro per fare la stessa cosa: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

Si dovrà aggiungere un progetto C ++ / CLI per la soluzione di Visual Studio però.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top