C# Marshal.SizeOf和sizeof的区别

今天在C#的程序中导入C++的dll时,发现C++中结构体里的bool类型变量是用的BOOL,在C++头文件中定义的是int为BOOL,占4个字节。C#中相应的结构体里用的却是bool类型,可是bool类型只占一个字节,于是就产生了疑问,为什么这样不会导致结构体所占的字节大小不同呢?

这是因为我们在封装定义结构体时,是非托管代码。我们在C#中直接sizeof(bool),得到的是1字节,但是如果我们Marshal.SizeOf(typeof(bool)),获取bool的非托管类型大小得到的就是4个字节。

 


Sizeof在非Unsafe环境下只能用于预定义的一系列类型,如Int,Short等等。而在Unsafe环境下,sizeof可以被用于值类型,但是值类型中不可以有引用类型,否则C#编译器会报错:

error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('SizeOf.Program.MyStruct')

而Marshal.SizeOf则是获得该类型被Marshal(转换,通常翻译为列集,指数据从一种类型转换到另外一种类型)到对应的非托管类型的大小。和sizeof不同,Marshal.SizeOf允许用在含有引用类型的值类型上:
 

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
     string s;
}

Marshal.SizeOf(MyStruct)结果为4或者8,因为string被Marshal成char*。

如果我们想指定string或者byte的字节长度,可以使用MarshalAs,MarshalAs属性指示如何在托管代码和非托管代码之间封送数据。

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]      // 指定string的长度用ByValTStr
    string s;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]     // 指定byte数组的长度用ByValArray
    byte []x;
}

如果用在不含有引用类型的值类型上,其结果也有可能和sizeof完全不一样,如对于下面的值类型:

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
     char b;
}

sizeof(MyStruct)为2,而Marshal.SizeOf(typeof(MyStruct))结果则为1。这是因为在.NET中char总是Unicode,而缺省情况下char会被Marshal成8位的Ansi字符,因此结果不同。

反之,如果我们指定这个char被Marshal成short值(也就是UTF16),如下:
 

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
     [MarshalAs(UnmanagedType.I2)]
     char b;
}

那么sizeof和Marshal.SizeOf结果均为2。MarshalAs这个Attribute可以影响Marshal.SizeOf的结果,而不能影响sizeof的结果。

一个有意思的情况是,如果值类型不含任何成员,如下:

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
}

Sizeof和Marshal.SizeOf结果均为1,而不是0。这个结果和C++的结果是一致的。原因很简单:如果声明一个这样的数组,如果元素大小为0的话,那么每个元素都具有相同的地址,这是不为C++标准所允许的,和正常的非0的情况也不一致。.NET在这里采用和c++相同的规则,也认为空的值类型大小为1。

最后需要注意的是,如果MyStruct是模板:

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
    T a
}

如果对Marshal.SizeOf传入MyStruct<>或者MyStruct这样的类型,则抛出ArgumentException,因为Marshal.SizeOf完全不支持泛型。

同样的,sizeof也不支持模板类型,而且连MyStruct这样子的类型也不支持。C#编译器会对sizeof(MyStruct)报错:error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('SizeOf.Program.MyStruct')
 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值