Obtener el tamaño de un campo en bytes con C #

Tengo una clase y quiero inspeccionar sus campos e informar finalmente cuántos bytes toma cada campo. Supongo que todos los campos son de tipo Int32, byte, etc.

¿Cómo puedo averiguar fácilmente cuántos bytes toma el campo?

Necesito algo como:

Int32 a; // int a_size = a.GetSizeInBytes; // a_size should be 4 

No puedes, básicamente. Dependerá del relleno, que bien puede basarse en la versión de CLR que está utilizando y el procesador, etc. Es más fácil calcular el tamaño total de un objeto, suponiendo que no tiene referencias a otros objetos: crear una gran matriz, use GC.GetTotalMemory para un punto base, llene la matriz con referencias a nuevas instancias de su tipo, y luego vuelva a llamar a GetTotalMemory. Quite un valor del otro y divida por el número de instancias. Probablemente deberías crear una sola instancia de antemano para asegurarte de que ningún código nuevo JIT contribuye al número. Sí, es tan hacky como suena, pero lo he usado con buenos resultados antes de ahora.

Ayer mismo estaba pensando que sería una buena idea escribir una pequeña clase de ayuda para esto. Avísame si te interesaría.

EDITAR: Hay otras dos sugerencias, y me gustaría abordarlas ambas.

En primer lugar, el operador sizeof : esto solo muestra la cantidad de espacio que ocupa el tipo en el resumen, sin aplicar ningún relleno alrededor de él. (Incluye relleno dentro de una estructura, pero no relleno aplicado a una variable de ese tipo dentro de otro tipo).

A continuación, Marshal.SizeOf : esto solo muestra el tamaño no administrado después de la clasificación, no el tamaño real en la memoria. Como la documentación declara explícitamente:

El tamaño devuelto es realmente el tamaño del tipo no administrado. Los tamaños no gestionados y gestionados de un objeto pueden diferir. Para los tipos de caracteres, el tamaño se ve afectado por el valor de CharSet aplicado a esa clase.

Y de nuevo, el relleno puede hacer la diferencia.

Solo para aclarar a qué me refiero sobre que el relleno sea relevante, considere estas dos clases:

 class FourBytes { byte a, b, c, d; } class FiveBytes { byte a, b, c, d, e; } 

En mi cuadro x86, una instancia de FourBytes toma 12 bytes (incluida la sobrecarga). Una instancia de FiveBytes toma 16 bytes. La única diferencia es la variable “e”, ¿así que eso toma 4 bytes? Bueno, más o menos … y algo así como no. Obviamente, podría eliminar cualquier variable de FiveBytes para obtener el tamaño de 12 bytes, pero eso no significa que cada una de las variables ocupe 4 bytes (¡piense en eliminarlas todas!). El costo de una sola variable no es un concepto que tenga mucho sentido aquí.

Dependiendo de las necesidades del entrevistado, Marshal.SizeOf podría darle o no lo que desea. (Editado después de que Jon Skeet publicó su respuesta).

 using System; using System.Runtime.InteropServices; public class MyClass { public static void Main() { Int32 a = 10; Console.WriteLine(Marshal.SizeOf(a)); Console.ReadLine(); } } 

Tenga en cuenta que, como dice jkersch, se puede usar sizeof, pero desafortunadamente solo con tipos de valores. Si necesita el tamaño de una clase, Marshal.SizeOf es el camino a seguir.

Jon Skeet ha explicado por qué ni sizeof ni Marshal.SizeOf son perfectos. Creo que el entrevistado necesita decidir si es aceptable para su problema.

De la receta de Jon Skeets en su respuesta intenté hacer la clase de ayudante a la que se refería. Sugerencias para mejorar son bienvenidas.

 public class MeasureSize { private readonly Func _generator; private const int NumberOfInstances = 10000; private readonly T[] _memArray; public MeasureSize(Func generator) { _generator = generator; _memArray = new T[NumberOfInstances]; } public long GetByteSize() { //Make one to make sure it is jitted _generator(); long oldSize = GC.GetTotalMemory(false); for(int i=0; i < NumberOfInstances; i++) { _memArray[i] = _generator(); } long newSize = GC.GetTotalMemory(false); return (newSize - oldSize) / NumberOfInstances; } } 

Uso:

Debe crearse con un Func que genere nuevas Instancias de T. Asegúrese de que la misma instancia no se devuelva cada vez. Ej. Esto estaría bien:

  public long SizeOfSomeObject() { var measure = new MeasureSize(() => new SomeObject()); return measure.GetByteSize(); } 

Se puede hacer indirectamente, sin considerar la alineación. El número de bytes que la instancia del tipo de referencia es igual al tamaño de los campos de servicio + escriba el tamaño de los campos. Campos de servicio (en 32x toma 4 bytes cada uno, 64x 8 bytes):

  1. Sysblockindex
  2. Tabla de puntero a métodos
  3. + Tamaño de matriz opcional (solo para matrices)

Entonces, para la clase sin ningún archivo, su instancia toma 8 bytes en la máquina 32x. Si es una clase con un campo, referencia en la misma instancia de clase, entonces, esta clase toma (64x):

Sysblockindex + pMthdTable + referencia en la clase = 8 + 8 + 8 = 24 bytes

Si es un tipo de valor, no tiene ningún campo de instancia, por lo tanto, solo toma su tamaño de archivo. Por ejemplo, si tenemos struct con un campo int, entonces en la máquina 32x solo lleva 4 bytes de memoria.

Tuve que reducir todo el camino hasta el nivel IL, pero finalmente logré esta funcionalidad en C # con una biblioteca muy pequeña.

Puede obtenerlo (licencia BSD) en bitbucket

Código de ejemplo:

 using Earlz.BareMetal; ... Console.WriteLine(BareMetal.SizeOf()); //returns 4 everywhere I've tested Console.WriteLine(BareMetal.SizeOf()); //returns 8 on 64-bit platforms and 4 on 32-bit Console.WriteLine(BareMetal.SizeOf()); //returns 16 in some places, 24 in others. Varies by platform and framework version ... struct Foo { int a, b; byte c; object foo; } 

Básicamente, lo que hice fue escribir un envoltorio rápido de método de clase en torno al sizeof instrucción IL. Esta instrucción obtendrá la cantidad bruta de memoria que utilizará una referencia a un objeto. Por ejemplo, si tiene una matriz de T , el sizeof instrucción le indicará cuántos bytes separa cada elemento de la matriz.

Esto es extremadamente diferente del operador sizeof de C #. Por un lado, C # solo permite tipos de valores puros porque no es realmente posible obtener el tamaño de cualquier otra cosa de manera estática. Por el contrario, el sizeof instrucción funciona en un nivel de tiempo de ejecución. Por lo tanto, sin importar cuánta memoria usaría una referencia a un tipo durante esta instancia en particular, se devolvería.

Puedes ver más información y un código de muestra un poco más detallado en mi blog

si tiene el tipo, use el operador sizeof. devolverá el tamaño del tipo en byte. p.ej

Console.WriteLine (sizeof (int));

dará salida:

4

Puede usar la sobrecarga de métodos como un truco para determinar el tamaño del campo:

 public static int FieldSize(int Field) { return sizeof(int); } public static int FieldSize(bool Field) { return sizeof(bool); } public static int FieldSize(SomeStructType Field) { return sizeof(SomeStructType); }