sizeof(T) 和 Unsafe.SizeOf<T>() 有啥区别?
Posted
技术标签:
【中文标题】sizeof(T) 和 Unsafe.SizeOf<T>() 有啥区别?【英文标题】:What's the difference between sizeof(T) and Unsafe.SizeOf<T>()?sizeof(T) 和 Unsafe.SizeOf<T>() 有什么区别? 【发布时间】:2017-12-21 10:09:17 【问题描述】:首先,在实际问题之前的一个小免责声明:
我知道关于
sizeof
运算符和Marshal.SizeOf<T>
方法之间的区别有很多封闭/重复的问题,我确实了解两者之间的区别。这里我说的是新的Unsafe
类中的SizeOf<T>
方法
因此,我不确定我是否了解这两个操作之间的实际区别,以及在特别是在结构/类上使用该方法时是否存在特定区别。
sizeof
运算符采用 类型名称 并返回分配时它应该占用的 托管字节数(即 Int32
将返回4,例如)。
另一方面,Unsafe.SizeOf<T>
方法是在 IL 中实现的,就像 Unsafe
类中的所有其他方法一样,看看下面的代码就是它的作用:
.method public hidebysig static int32 SizeOf<T>() cil managed aggressiveinlining
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 1
sizeof !!T
ret
现在,如果我没记错的话,代码只是调用sizeof !!T
,这与sizeof(T)
相同(使用类型名称T
调用sizeof
运算符),所以两者不会它们中的哪一个是完全等价的?
另外,我看到该方法还在第一行分配了一个无用的对象(NonVersionableAttribute
),那么这不会导致少量内存也被堆分配吗?
我的问题是:
可以肯定地说这两种方法是完全等价的,因此最好使用经典的
sizeof
运算符,因为这样也可以避免在SizeOf<T>
方法中分配该属性?这个SizeOf<T>
方法是否添加到Unsafe
类中只是为了方便?
【问题讨论】:
.custom
语句只是告诉您存在属于此方法的自定义属性。由于自定义属性只是元数据,它们不参与常规方法调用。
只是澄清一下,这是我不知道的 BCL 类,还是 third party extension?你说得对,它什么都做不了,也许这个想法是为将来的一些跨平台魔法提供一个“钩子”?
@Groo 这是一个添加到 CoreFX 的新类,我相信 .NET Core 2.0 (不完全确定,但无论如何它都存在于 .NET Core 2.0 中)。见这里:docs.microsoft.com/it-it/dotnet/api/…
这是实现 System.Memory 的必要技巧。 coreclr 跟踪器问题is here。一个纯粹的实现细节,它返回的值取决于运行时使用的抖动。 C# 编译器仍然拒绝任何获取非 blittable 类型大小的尝试。
【参考方案1】:
虽然此方法确实只使用sizeof
IL 指令 - 与常规的sizeof
运算符有所不同,因为此运算符不能应用于任意类型:
用于获取非托管类型的大小(以字节为单位)。非托管 类型包括表中列出的内置类型 如下,还有以下:
枚举类型
指针类型
不包含任何内容的用户定义结构 引用类型的字段或属性
如果您尝试编写 Unsafe.SizeOf
的模拟 - 它不会起作用:
public static int SizeOf<T>()
// nope, will not compile
return sizeof(T);
所以Unsafe.SizeOf
解除了sizeof
运算符的限制,并允许您使用任意类型的IL sizeof
指令(包括它将返回引用大小的引用类型)。
至于你在 IL 中看到的属性构造——这并不意味着每次调用都会实例化属性——这只是用于将属性与各种成员相关联的 IL 语法(本例中的方法)。
例子:
public struct Test
public int Int1;
static void Main()
// works
var s1 = Unsafe.SizeOf<Test>();
// doesn't work, need to mark method with "unsafe"
var s2 = sizeof(Test);
另一个例子:
public struct Test
public int Int1;
public string String1;
static unsafe void Main()
// works, return 16 in 64bit process - 4 for int, 4 for padding, because
// alignment of the type is the size of its largest element, which is 8
// and 8 for string
var s1 = Unsafe.SizeOf<Test>();
// doesn't work even with unsafe,
// cannot take size of variable of managed type "Test"
// because Test contains field of reference type (string)
var s2 = sizeof(Test);
【讨论】:
完美,谢谢!虽然我们需要这个方法来做到这一点有点奇怪,但我想知道为什么他们选择不更新 C# 编译器让sizeof
运算符在没有额外的 IL 方法的情况下做同样的事情。另外,等一下,int
(因为它是Int32
结构)不应该总是 4 字节长,即使是 64 位进程? Test
的大小不应该是 12 字节吗?
@Sergio0694 是的,当然,int 是 4 个字节,另外 4 个字节是填充,修复了这个问题。至于为什么不更改 sizeof 运算符 - 我当然不知道,但我想这需要更改编译器,Unsafe.SizeOf
不需要任何更改。另外,让sizeof
那样工作首先是有原因的,所以我想你需要真正知道在使用Unsafe.SizeOf
时你在做什么(毕竟这是不安全的)。
有道理,感谢您的额外解释!完全忘记了字段偏移对齐,自从我上次用 C 编码以来已经有一段时间了,使用托管语言时你往往会忘记这些微小的细节啊哈哈哈
@Sergio0694 它被称为 Unsafe 是有原因的。最好使用安全的语言和可能不安全的 API,而不是单独使用不安全的语言。以上是关于sizeof(T) 和 Unsafe.SizeOf<T>() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章