sizeof(T) 和 Unsafe.SizeOf<T>() 有什么区别?

What's the difference between sizeof(T) and Unsafe.SizeOf<T>()?

首先,在实际问题之前的一个小免责声明:

I know there are a lot of closed/duplicate questions regarding the difference between the sizeof operator and the Marshal.SizeOf<T> method, and I do understand the difference between the two. Here I'm talking about the SizeOf<T> method in the new Unsafe class

所以,我不确定我是否理解这两个操作之间的实际差异,以及在特定 struct/class 上使用该方法时是否存在特定差异。

sizeof 运算符采用 类型名称 和 return 的 管理字节数 它是应该在分配时占用(例如,Int32 将 return 4)。

另一方面,Unsafe.SizeOf<T> 方法像 Unsafe class 中的所有其他方法一样在 IL 中实现,查看代码是它的作用:

.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) 相同(调用类型名称为 [=24 的 sizeof 运算符=]), 那么他们两个不是完全等价的吗?

此外,我看到该方法还在第一行分配了一个无用的对象(NonVersionableAttribute),所以这不会导致少量内存也被分配到堆中吗?

我的问题是:

Is it safe to say that the two methods are perfectly equivalent and that therefore it is just better to use the classic sizeof operator, as that also avoid the allocation of that attribute in the SizeOf<T> method? Was this SizeOf<T> method added to the Unsafe class just for convenience at this point?

虽然此方法确实只使用 sizeof IL 指令 - 与常规 sizeof 运算符有所不同,因为此运算符不能应用于任意类型:

Used to obtain the size in bytes for an unmanaged type. Unmanaged types include the built-in types that are listed in the table that follows, and also the following:

Enum types

Pointer types

User-defined structs that do not contain any fields or properties that are reference types

如果您尝试编写 Unsafe.SizeOf 的模拟 - 它不会工作:

public static int SizeOf<T>()
{
    // nope, will not compile
    return sizeof(T);
}

因此 Unsafe.SizeOf 解除了 sizeof 运算符的限制并允许您使用具有任意类型的 IL sizeof 指令(包括引用类型,它将 return 大小参考)。

至于您在 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);                        
}