使用 C# 获取字段的大小(以字节为单位)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/207592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getting the size of a field in bytes with C#
提问by
I have a class, and I want to inspect its fields and report eventually how many bytes each field takes. I assume all fields are of type Int32, byte, etc.
我有一个类,我想检查它的字段并最终报告每个字段占用的字节数。我假设所有字段都是 Int32、字节等类型。
How can I find out easily how many bytes does the field take?
如何轻松找出该字段占用的字节数?
I need something like:
我需要类似的东西:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
回答by Joachim Kerschbaumer
if you have the type, use the sizeof operator. it will return the type`s size in byte. e.g.
如果您有类型,请使用 sizeof 运算符。它将以字节为单位返回类型的大小。例如
Console.WriteLine(sizeof(int));
Console.WriteLine(sizeof(int));
will output:
将输出:
4
4
回答by Lasse V. Karlsen
Depending on the needs of the questionee, Marshal.SizeOf might or might not give you what you want. (Edited after Jon Skeet posted his answer).
根据被试者的需要,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();
}
}
Note that, as jkersch says, sizeof can be used, but unfortunately only with value types. If you need the size of a class, Marshal.SizeOf is the way to go.
请注意,正如 jkersch 所说,可以使用 sizeof,但不幸的是只能用于值类型。如果你需要一个类的大小, Marshal.SizeOf 是要走的路。
Jon Skeet has laid out why neither sizeof nor Marshal.SizeOf is perfect. I guess the questionee needs to decide wether either is acceptable to his problem.
Jon Skeet 阐述了为什么 sizeof 和 Marshal.SizeOf 都不完美。我猜被问者需要决定他的问题是否可以接受。
回答by Jon Skeet
You can't, basically. It will depend on padding, which may well be based on the CLR version you're using and the processor etc. It's easier to work out the total size of an object, assuming it has no references to other objects: create a big array, use GC.GetTotalMemoryfor a base point, fill the array with references to new instances of your type, and then call GetTotalMemory again. Take one value away from the other, and divide by the number of instances. You should probably create a single instance beforehand to make sure that no new JITted code contributes to the number. Yes, it's as hacky as it sounds - but I've used it to good effect before now.
你不能,基本上。这将取决于填充,这很可能基于您使用的 CLR 版本和处理器等。假设它没有对其他对象的引用,计算出一个对象的总大小会更容易:创建一个大数组,使用GC.GetTotalMemory作为基点,使用对您的类型的新实例的引用填充数组,然后再次调用 GetTotalMemory。从另一个值中取出一个值,然后除以实例数。您可能应该事先创建一个实例,以确保没有新的 JITted 代码对数字有贡献。是的,它听起来很老套——但我以前用过它,效果很好。
Just yesterday I was thinking it would be a good idea to write a little helper class for this. Let me know if you'd be interested.
就在昨天,我在想为此编写一个小助手类是个好主意。如果您有兴趣,请告诉我。
EDIT: There are two other suggestions, and I'd like to address them both.
编辑:还有另外两个建议,我想同时解决它们。
Firstly, the sizeofoperator: this only shows how much space the type takes up in the abstract, with no padding applied round it. (It includes padding within a structure, but not padding applied to a variable of that type within another type.)
首先是sizeof运算符:这仅显示该类型在抽象中占用了多少空间,而没有在其周围应用填充。(它包括结构内的填充,但不包括应用于另一种类型中该类型变量的填充。)
Next, Marshal.SizeOf: this only shows the unmanaged size after marshalling, not the actual size in memory. As the documentation explicitly states:
接下来,Marshal.SizeOf:这仅显示编组后的非托管大小,而不是内存中的实际大小。正如文档明确指出的那样:
The size returned is the actually the size of the unmanaged type. The unmanaged and managed sizes of an object can differ. For character types, the size is affected by the CharSet value applied to that class.
返回的大小是非托管类型的实际大小。对象的非托管和托管大小可能不同。对于字符类型,大小受应用于该类的 CharSet 值的影响。
And again, padding can make a difference.
再一次,填充可以有所作为。
Just to clarify what I mean about padding being relevant, consider these two classes:
为了澄清我关于填充相关的意思,请考虑以下两个类:
class FourBytes { byte a, b, c, d; }
class FiveBytes { byte a, b, c, d, e; }
On my x86 box, an instance of FourBytes takes 12 bytes (including overhead). An instance of FiveBytes takes 16 bytes. The only difference is the "e" variable - so does that take 4 bytes? Well, sort of... and sort of not. Fairly obviously, you could remove any single variable from FiveBytes to get the size back down to 12 bytes, but that doesn't mean that eachof the variables takes up 4 bytes (think about removing all of them!). The cost of a single variable just isn't a concept which makes a lot of sense here.
在我的 x86 机器上,FourBytes 的一个实例占用 12 个字节(包括开销)。FiveBytes 的一个实例需要 16 个字节。唯一的区别是“e”变量 - 那么它需要 4 个字节吗?嗯,有点……有点不是。很明显,您可以从 FiveBytes 中删除任何单个变量以将大小减小到 12 个字节,但这并不意味着每个变量占用 4 个字节(考虑删除所有变量!)。单个变量的成本并不是一个在这里很有意义的概念。
回答by Sergey Popov
It can be done indirectly, without considering the alignment. The number of bytes that reference type instance is equal service fields size + type fields size. Service fields(in 32x takes 4 bytes each, 64x 8 bytes):
它可以间接完成,无需考虑对齐。引用类型实例的字节数等于服务字段大小+类型字段大小。服务字段(在 32x 中每个需要 4 个字节,64x 8 个字节):
- Sysblockindex
- Pointer to methods table
- +Optional(only for arrays) array size
- 系统块索引
- 指向方法表的指针
- +可选(仅适用于数组)数组大小
So, for class without any fileds, his instance takes 8 bytes on 32x machine. If it is class with one field, reference on the same class instance, so, this class takes(64x):
因此,对于没有任何文件的类,他的实例在 32x 机器上占用 8 个字节。如果是一个字段的类,在同一个类实例上引用,所以,这个类需要(64x):
Sysblockindex + pMthdTable + reference on class = 8 + 8 + 8 = 24 bytes
Sysblockindex + pMthdTable + 类参考 = 8 + 8 + 8 = 24 字节
If it is value type, it does not have any instance fields, therefore in takes only his fileds size. For example if we have struct with one int field, then on 32x machine it takes only 4 bytes memory.
如果是值类型,则没有任何实例字段,因此 in 只取其字段大小。例如,如果我们有一个带有一个 int 字段的结构体,那么在 32x 机器上它只需要 4 个字节的内存。
回答by Jesper Niedermann
From Jon Skeets recipe in his answer I tried to make the helper class he was refering to. Suggestions for improvements are welcome.
从 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;
}
}
Usage:
用法:
Should be created with a Func that generates new Instances of T. Make sure the same instance is not returned everytime. E.g. This would be fine:
应该使用生成 T 的新实例的 Func 创建。确保不会每次都返回相同的实例。例如,这很好:
public long SizeOfSomeObject()
{
var measure = new MeasureSize<SomeObject>(() => new SomeObject());
return measure.GetByteSize();
}
回答by Earlz
I had to boil this down all the way to IL level, but I finally got this functionality into C# with a very tiny library.
我不得不一直将其归结为 IL 级别,但我最终通过一个非常小的库将这个功能引入了 C#。
You can get it (BSD licensed) at bitbucket
您可以在bitbucket获得它(BSD 许可)
Example code:
示例代码:
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;
}
Basically, what I did was write a quick class-method wrapper around the sizeof
IL instruction. This instruction will get the raw amount of memory a reference to an object will use. For instance, if you had an array of T
, then the sizeof
instruction would tell you how many bytes apart each array element is.
基本上,我所做的是围绕sizeof
IL 指令编写一个快速的类方法包装器。此指令将获取对对象的引用将使用的原始内存量。例如,如果您有一个 数组T
,那么sizeof
指令会告诉您每个数组元素相隔多少字节。
This is extremely different from C#'s sizeof
operator. For one, C# only allows pure value types because it's not really possible to get the size of anything else in a static manner. In contrast, the sizeof
instruction works at a runtime level. So, however much memory a reference to a type would use during this particular instance would be returned.
这与 C# 的sizeof
运算符截然不同。一方面,C# 只允许纯值类型,因为实际上不可能以静态方式获取其他任何东西的大小。相比之下,该sizeof
指令在运行时级别工作。因此,无论在此特定实例期间对类型的引用将使用多少内存,都将返回。
You can see some more info and a bit more in-depth sample code at my blog
您可以在我的博客上看到更多信息和更深入的示例代码
回答by David Chen
You can use method overloading as a trick to determine the field size:
您可以使用方法重载作为确定字段大小的技巧:
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); }
回答by TakeMeAsAGuest
Simplest way is: int size = *((int*)type.TypeHandle.Value + 1)
最简单的方法是: int size = *((int*)type.TypeHandle.Value + 1)
I know this is implementation detail but GC relies on it and it needs to be as close to start of the methodtable for efficiency plus taking into consideration how GC code complex is nobody will dare to change it in future. In fact it works for every minor/major versions of .net framework+.net core. (Currently unable to test for 1.0)
If you want more reliable way, emit a struct in a dynamic assembly with [StructLayout(LayoutKind.Auto)]
with exact same fields in same order, take its size with sizeofIL instruction. You may want to emit a static method within struct which simply returns this value. Then add 2*IntPtr.Size for object header. This should give you exact value.
But if your class derives from another class, you need to find each size of base class seperatly and add them + 2*Inptr.Size again for header. You can do this by getting fields with BindingFlags.DeclaredOnly
flag.
我知道这是实现细节,但 GC 依赖于它,它需要尽可能接近方法表的开头以提高效率,并考虑到 GC 代码的复杂性,将来没人敢改变它。事实上,它适用于 .net framework+.net core 的每个次要/主要版本。(目前无法测试 1.0)
如果您想要更可靠的方式,请在动态程序集中发出具有[StructLayout(LayoutKind.Auto)]
相同顺序的完全相同字段的结构,请使用sizeofIL 指令获取其大小。您可能希望在 struct 中发出一个静态方法,该方法仅返回此值。然后为对象头添加 2*IntPtr.Size 。这应该给你确切的价值。
但是如果你的类派生自另一个类,你需要单独找到基类的每个大小并再次添加它们 + 2*Inptr.Size 作为标题。您可以通过获取带有BindingFlags.DeclaredOnly
标志的字段来做到这一点。
回答by Bruno Zell
System.Runtime.CompilerServices.Unsafe
System.Runtime.CompilerServices.Unsafe
Use System.Runtime.CompilerServices.Unsafe.SizeOf<T>() where T: unmanaged
用 System.Runtime.CompilerServices.Unsafe.SizeOf<T>() where T: unmanaged
(when not running in .NET Core you need to install that NuGet package)
(当不在 .NET Core 中运行时,您需要安装该 NuGet 包)
Documentationstates:
文档说明:
Returns the size of an object of the given type parameter.
返回给定类型参数的对象的大小。
It seems to use the sizeof
IL-instruction just as Earlz solutiondoes as well. (source)
它似乎使用sizeof
IL 指令,就像Earlz 解决方案一样。(来源)
The unmanaged
constraint is new in C# 7.3
该unmanaged
约束是 C# 7.3 中的新增内容