我有一个类,想要检查它的字段并报告每个字段占用了多少字节。假设所有字段的类型都是Int32、byte等。
如何轻松地找出每个字段占用了多少字节?
我需要像这样的信息:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
我有一个类,想要检查它的字段并报告每个字段占用了多少字节。假设所有字段的类型都是Int32、byte等。
如何轻松地找出每个字段占用了多少字节?
我需要像这样的信息:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
返回的大小实际上是未托管类型的大小。对象的托管和未托管大小可能不同。对于字符类型,大小受应用于该类的CharSet值的影响。
再次强调,填充可能会产生差异。
为了澄清我所说的填充有关,考虑这两个类:
class FourBytes { byte a, b, c, d; }
class FiveBytes { byte a, b, c, d, e; }
根据提问者的需求,Marshal.SizeOf 可能会或可能不会给你想要的结果。(在 Jon Skeet 发布他的答案后进行了编辑)。
using System;
using System.Runtime.InteropServices;
public class MyClass
{
public static void Main()
{
Int32 a = 10;
Console.WriteLine(Marshal.SizeOf(a));
Console.ReadLine();
}
}
请注意,正如jkersch所说,可以使用sizeof,但可惜的是只能用于值类型。如果您需要类的大小,则应使用Marshal.SizeOf。
Jon Skeet已经说明了为什么sizeof和Marshal.SizeOf都不完美。我猜测提问者需要决定是否接受其中任何一个来解决他的问题。
根据Jon Skeet在他的回答中提到的方法,我尝试制作了助手类。欢迎提出改进建议。
public class MeasureSize<T>
{
private readonly Func<T> _generator;
private const int NumberOfInstances = 10000;
private readonly T[] _memArray;
public MeasureSize(Func<T> 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;
}
}
使用方式:
应该使用生成新的T实例的Func来创建。确保不要每次返回相同的实例。例如,以下方法是正确的:
public long SizeOfSomeObject()
{
var measure = new MeasureSize<SomeObject>(() => new SomeObject());
return measure.GetByteSize();
}
_memArray
的初始内存分配。也就是说,在_memArray = new T[NumberOfInstances];
之前测量oldSize
是有意义的。 - Alexey Khoroshikhusing Earlz.BareMetal;
...
Console.WriteLine(BareMetal.SizeOf<int>()); //returns 4 everywhere I've tested
Console.WriteLine(BareMetal.SizeOf<string>()); //returns 8 on 64-bit platforms and 4 on 32-bit
Console.WriteLine(BareMetal.SizeOf<Foo>()); //returns 16 in some places, 24 in others. Varies by platform and framework version
...
struct Foo
{
int a, b;
byte c;
object foo;
}
sizeof
IL指令周围编写了一个快速的类方法包装器。该指令将获取引用对象使用的原始内存量。例如,如果您有一个T
数组,则sizeof
指令将告诉您每个数组元素之间相隔多少字节。sizeof
运算符非常不同。首先,C#只允许纯值类型,因为静态方式无法获取其他任何内容的大小。相比之下,sizeof
指令在运行时级别工作。因此,在此特定实例期间使用对类型的引用将返回使用的内存量。byte
会是什么”,因为在不同的上下文中它不同。但是,这肯定会有所帮助。(如果其他方法都失败了,可以逐个字段地向结构添加字段,并通过此方法测量每个字段的大小) - EarlzDynamicMethod
实现简单的IL
非常容易;您不需要一个完整的独立库/ dll。例如,请查看此答案,它还提供了另一种方法来回答ValueType
- 或者更有趣的是,“Formatted Class” - in-situ大小问题。在那个答案中,我提供了一个通用的托管指针减法函数,在IL
中。它返回一个字节偏移量,因此支持与不同类型一起使用,因此它也可以测量任何结构体的大小,只要给定的东西被知道是pack=1
相邻的。 - Glenn SlaydenSysblockindex + pMthdTable + 类的引用 = 8 + 8 + 8 = 24个字节
如果它是值类型,则不具有任何实例字段,因此仅占据其字段大小。例如,如果我们有一个具有一个整型字段的结构体,则在32位机器上只需占用4个字节的内存。Console.WriteLine(sizeof(int));
将输出:
4
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); }
使用 System.Runtime.CompilerServices.Unsafe.SizeOf<T>() where T: unmanaged
(当不在 .NET Core 中运行时,您需要安装该 NuGet 包)
文档 表示:
返回给定类型参数的对象大小。
它似乎像 Earlz 解决方案 一样使用了 sizeof
IL 指令。(来源)
unmanaged
约束是 C# 7.3 中的新功能。
最简单的方法是:int size = *((int*)type.TypeHandle.Value + 1)
我知道这是实现细节,但GC依赖它,并且为了效率,它需要尽可能靠近方法表的开头,考虑到GC代码的复杂性,未来没有人敢改变它。事实上,它适用于每个 .net framework+.net core 的次要/主要版本。(目前无法测试1.0)
如果您想要更可靠的方法,请使用[StructLayout(LayoutKind.Auto)]
在动态程序集中发出一个结构体,具有相同顺序的完全相同字段,使用sizeof IL指令获取其大小。您可能需要在结构体中发出一个静态方法,该方法仅返回此值。然后添加2 * IntPtr.Size以获取对象标头。这应该给您确切的值。
但是,如果您的类派生自另一个类,则需要分别找到每个基类的大小,并再次添加它们+ 2 * Inptr.Size以获取标头。您可以使用BindingFlags.DeclaredOnly
标志获取字段来执行此操作。
ldelema
怎么样?如果你深入到 IL 级别,那么ldelema
是否可靠地确定给定对象的大小呢?(如果你取出 T 数组中相邻两个元素的地址,然后得到它们之间的差异)...或者说,sizeof
指令应该正好做到这个问题所要求的。 - Earlzsizeof
与IL中的sizeof
指令完全不同。 - Earlz