我有一个类,我想检查它的字段并最终报告每个字段占用的字节数。我假设所有字段都是Int32,byte等类型。
如何轻松找出该字段占用的字节数?
我需要类似的东西:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
答案 0 :(得分:98)
就在昨天,我认为为此编写一个小助手类是个好主意。如果您有兴趣,请告诉我。
编辑:还有另外两个建议,我想解决它们。
首先,sizeof运算符:这只显示了抽象中类型占用了多少空间,没有应用填充。 (它包括结构中的填充,但不是在另一种类型中应用于该类型的变量的填充。)
接下来,Marshal.SizeOf:这只显示编组后的非托管大小,而不是内存中的实际大小。正如文档明确指出:
返回的大小实际上是 非托管类型的大小。该 非托管和托管的大小 对象可以不同。对于角色 类型,大小受到影响 CharSet值应用于该类。
再次,填充可以有所作为。
为了澄清我对填充相关的含义,请考虑以下两个类:
class FourBytes { byte a, b, c, d; }
class FiveBytes { byte a, b, c, d, e; }
在我的x86盒子上,一个FourBytes实例需要12个字节(包括开销)。 FiveBytes的实例需要16个字节。唯一的区别是“e”变量 - 那么需要4个字节吗?好吧,有点......而不是。很明显,您可以从FiveBytes中删除任何单个变量以将大小缩减到12个字节,但这并不意味着每个变量占用4个字节(考虑删除所有这些变量) !)。单个变量的成本并不是一个在这里很有意义的概念。
答案 1 :(得分:17)
根据被问者的需要,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都不是完美的。我猜问题需要决定是否可以接受他的问题。
答案 2 :(得分:8)
来自Jon Skeets的回答中,我尝试制作他所指的帮助班。欢迎提出改进建议。
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();
}
答案 3 :(得分:5)
我不得不把它一直放到IL级别,但我终于将这个功能带到了C#中,带有一个非常小的库。
您可以在bitbucket
获取(BSD许可)示例代码:
using 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
指令会告诉你每个数组元素之间有多少字节。
这与C#的sizeof
运营商截然不同。首先,C#只允许纯值类型,因为它不可能以静态方式获得其他任何东西的大小。相反,sizeof
指令在运行时级别工作。因此,无论多少内存,都会返回在此特定实例期间将使用的类型引用。
您可以在我的blog
中查看更多信息和更深入的示例代码答案 4 :(得分:4)
可以间接完成,不考虑对齐。 引用类型实例的字节数相等服务字段大小+类型字段大小。 服务字段(32x每个占用4个字节,64x 8个字节):
因此,对于没有任何文件的类,他的实例在32x机器上占用8个字节。如果它是具有一个字段的类,则引用同一个类实例,因此,此类采用(64x):
Sysblockindex + pMthdTable +类= 8 + 8 + 8 = 24字节的引用
如果它是值类型,则它没有任何实例字段,因此仅采用他的文件大小。例如,如果我们有一个带有一个int字段的struct,那么在32x机器上它只需要4个字节的内存。
答案 5 :(得分:1)
如果您有类型,请使用sizeof运算符。它将以字节为单位返回类型的大小。 e.g。
Console.WriteLine(的sizeof(int)的);
将输出:
4
答案 6 :(得分:0)
您可以使用方法重载作为确定字段大小的技巧:
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); }
答案 7 :(得分:0)
最简单的方法是:int size = *((int*)type.TypeHandle.Value + 1)
我知道这是实现细节,但是GC依赖它,因此它必须尽可能接近方法表的开头,以提高效率,还要考虑到GC代码复杂性是谁将来都不会敢更改的。实际上,它适用于.net framework + .net核心的每个次要/主要版本。 (当前无法测试1.0)
如果需要更可靠的方法,请在动态程序集中使用[StructLayout(LayoutKind.Auto)]
发出结构,并以相同的顺序完全相同的字段,并使用sizeof IL指令获取其大小。您可能希望在struct中发出一个静态方法,该方法仅返回此值。然后为对象标题添加2 * IntPtr.Size。这应该给您确切的价值。
但是,如果您的类是从另一个类派生的,则需要分别查找基类的每个大小,然后再次为标头添加它们+ 2 * Inptr.Size。您可以通过获取带有BindingFlags.DeclaredOnly
标志的字段来做到这一点。
答案 8 :(得分:0)
使用System.Runtime.CompilerServices.Unsafe.SizeOf<T>() where T: unmanaged
(当不在.NET Core中运行时,您需要安装该NuGet软件包)
返回给定类型参数的对象的大小。
似乎也使用sizeof
IL指令,就像Earlz solution一样。 (source)
unmanaged
约束在C#7.3中是新的