sizeof(T)
从C++的模板代码往C#代码移植的时候发现了一个小问题。
在C++模板代码中 sizeof(T)是一种有效的写法,最终在会编译器展开成sizeof(int),sizeof(float)或者sizeof(myclass),然后在运行时这个代码是有效的,能够执行的。于是我们看上去就可以计算在运行时计算T的大小,并分配内存。
但是在C#的泛型代码中,sizeof(T)无法编译过的,因为无法确认T是什么的情况下,T的大小是无法计算的,于是C#编译器是不认的。
按C#提供的规范,sizeof只能在不安全的代码中使用,操作的参数是非托管类型。
# 非托管类型
sbyte
、
byte
、
short
、
ushort
、
int
、
uint
、
long
、
ulong
、
char
、
float
、
double
、
decimal
或
bool
任何枚举类型
任何指针类型
任何用户定义的
struct
类型,只包含非托管类型的字段,并且在 C
# 7.3
及更早版本中,不是构造类型(包含至少一个类型参数的类型)
那么在C#的泛型类里面,该如何进行sizeof(T)操作?
然后我开始尝试着在.net的开源代码里面寻找答案
OK,一下子找到两个
Unsafe.SizeOf
和
Marshal.SizeOf
Unsafe.SizeOf
Unsafe.SizeOf 属于 CompilerServices,继续挖掘代码,最后得到了一段IL Code
.method
public
hidebysig
static
int32 SizeOf<T>
cil managed aggressiveinlining
//....
.maxstack
1
sizeof
!!T
}
// end of method Unsafe::SizeOf
OK,这是 IL语言,我们就看自己关心的sizeof
#IL Code 说明
sizeof
将提供的值类型的大小(以字节为单位)推送到计算堆栈上。
OK,UnsafeSizeOf 只有值类型的大小
Unsafe
.SizeOf
<
myclass
>
;
不管我怎么改变myclass的内容,结果都是8,所以,这个不能随便用,只能用在值类型上了。
结合C#的文档,我蛮怀疑关键字sizeof要么是调用了Unsafe.Sizeof函数,要么就是直接转换成了IL Code的sizeof。但没什么依据,我没在.net的源代码里找到这一点。
Marshal.SizeOf
这个属于 System.Runtime.InteropServices,是.net 和COM互操作的时候用的。
这个函数挖掘代码之后是到了一些cpp代码,基本路径是这样的
Marshal.SizeOf->SizeOfHelper->
MarshalNative::SizeOfClass->GetNativeSize
Marshal.SizeOf->SizeOfHelper是C#代码
GetNativeSize是用C++代码实现的,两边怎么焊接的我就不管了,这个暂时不关心。
BOOL
GetNativeSize
const
LIMITED_METHOD_CONTRACT;
return
m_cbNativeSize;
返回了一个m_cbNativeSize;
OK,我们实际测试下
Marshal
.SizeOf
<
myclass
>;
直接报错了,因为不是一个非托管结构,没法计算大小。
Type
'ConsoleApp1.Program+myclass'
cannot be marshaled
as
an unmanaged structure;
no
meaningful size
or
offset can be computed.
OK,稍微调整下代码
加一个
[StructLayout(LayoutKind.Sequential)]
然后输出结果是12,刚好是三个int的大小。
等下,还有第三种办法:从 C# 7.3 开始,可使用 unmanaged 约束指定:类型参数为“非指针、不可为 null 的非托管类型”。从 C# 8.0 开始,仅包含非托管类型的字段的构造结构类型也是非托管类型,如以下示例所示:
public
struct
Coords<T>
public
T X;
public
T Y;
public
class
UnmanagedTypes
public
static
void
Main
(
)
DisplaySize<Coords<
int
>>;
DisplaySize<Coords<
double
>>;
private
unsafe
static
void
DisplaySize<T>
where
T : unmanaged
Console.WriteLine(
$"
{
typeof
(T)}
is unmanaged and its size is
{
sizeof
(T)}
bytes"
);
// Output:
// Coords`1[System.Int32] is unmanaged and its size is 8 bytes
// Coords`1[System.Double] is unmanaged and its size is 16 bytes
那么回到我开始的问题,如果都是值类型,两个都可以用,如果是自定义类,用Marshal.SizeOf就可以了
或者把
泛
型类写成
public
struct
Coords
<T>
where
T
:
unmanaged
public
T X;
public
T Y;
即T只限于非托管类型。
1.值类型
泛
型类,可以用
where
T : unmanaged 和
sizeof
(T)配合使用
或者直接使用 Unsafe.Sizeof(T)
2.非值类型泛型类,可用
Marshal
.SizeOf
(
T
)
本文结束。
------------------------------
写在文后:昨天发出之后发现把“泛”字打成范了,想想还是删除了今天再发,写一篇技术文章从选题,写稿,编辑,检查错别字,修改,
到发稿
来来回回几个小时。没有一件事情是容易的。我尽量严肃对待这一切,让各位关注者能有所得。谢谢。
------------------------------
写多不如写精!
我是丁长老,
欢迎关注我的技术公众号。
个人微信号nine-ding,欢迎加我微信。
如需转发文章请加微信号获取转发授权
返回搜狐,查看更多
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。