Pregunta

Me he encontrado recientemente una situación en la que tengo que crear un método genérico para leer un tipo de datos de una matriz de bytes.

He creado la siguiente clase:


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

Ahora, los problemas de asignación de-aparte, este código no se compila con este mensaje:

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

Lo cual, parece extraño ya que debe ser capaz de hacer las operaciones anteriores basado en la restricción where T : struct en el método.

Si este código es terriblemente incorrecto, ¿hay alguna forma sencilla de tomar una serie de bytes y los arrojó a un tipo 'T'?

Gracias!

¿Fue útil?

Solución

En lugar de tratar de hacerlo a través de la manipulación del puntero, debe cambiar el código para utilizar Mashal.PtrToStructure . Este método está diseñado específicamente para este escenario.

Otros consejos

Dado que la respuesta ya se ha dado, permítanme explicar por qué su código original no funciona para usted:

  

Lo cual, parece extraño ya que debe ser capaz de hacer las operaciones anteriores en base a la que T:. Struct limitación en el método

En realidad no. Usted puede tener punteros primas a tipos no administrados . Esta se define como sigue en la especificación del lenguaje C # (18.2):

  

A diferencia de referencias (valores de los tipos de referencia), los punteros no son rastreados por el recolector de basura - el recolector de basura no tiene conocimiento de los punteros y los datos a los que apuntan. Por esta razón, un puntero no se le permite apuntar a una referencia o a una estructura que contiene referencias, y el tipo referente de un puntero debe ser un de tipo no administrado .   Un de tipo no administrado es cualquier tipo que no es un tipo de referencia y no contiene campos de tipo de referencia en cualquier nivel de anidamiento. En otras palabras, un no administrado de tipo es uno de los siguientes:

     
      
  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, o bool.
  •   
  • Cualquiera -tipo de enumeración .
  •   
  • Cualquiera de tipo puntero .
  •   
  • Cualquier definida por el usuario estructura de tipo que contiene campos de no administrados tipos solamente.
  •   

Así que hay un buen número de restricciones, y por un método genérico, T:struct pueden o no se ajustan a ellos para cualquier instanciación en particular, así como construir T* es ilegal. Sería bueno tener una restricción especial a los parámetros de tipo genérico para cubrir tipos no administrados, pero tal como está, no hay uno en el CLR.

En un momento escribí este artículo explicando cómo hacer exactamente eso , pero muchas veces más rápido que el Marshal.PtrToStructure. El ejemplo de código utiliza la generación dinámica de código para copiar el tipo T genérico a / de flujo de bits.

Suponiendo:

  • Una estructura secuencial o explícita (por lo demás muy mala idea)
  • tamaños datos correctos (que debe pre-check y tirar)

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

he escrito este hace un tiempo a hacer la misma cosa: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

Vas a tener que añadir un proyecto en C ++ / CLI para la solución de Visual Studio sin embargo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top