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允許用在含有引用類型的值類型上:
1: [StructLayout(LayoutKind.Sequential)]
2: struct MyStruct
3: {
4: string s;
5: }
Marshal.SizeOf(MyStruct)結果爲4或者8,因爲string被Marshal成char*。
如果用在不含有引用類型的值類型上,其結果也有可能和sizeof完全不一樣,如對於下面的值類型:
1: struct MyStruct
2: {
3: char b;
4: }
sizeof(MyStruct)爲2,而Marshal.SizeOf(typeof(MyStruct))結果則爲1。這是因爲在.NET中char總是Unicode,而缺省情況下char會被Marshal成8位的Ansi字符,因此結果不同。
反之,如果我們指定這個char被Marshal成short值(也就是UTF16),如下:
1: [StructLayout(LayoutKind.Sequential)]
2: struct MyStruct
3: {
4: [MarshalAs(UnmanagedType.I2)]
5: char b;
6: }
那麼sizeof和Marshal.SizeOf結果均爲2。MarshalAs這個Attribute可以影響Marshal.SizeOf的結果,而不能影響sizeof的結果。
一個有意思的情況是,如果值類型不含任何成員,如下:
1: struct MyStruct
2: {
3: }
Sizeof和Marshal.SizeOf結果均爲1,而不是0。這個結果和C++的結果是一致的。原因很簡單:如果聲明一個這樣的數組,如果元素大小爲0的話,那麼每個元素都具有相同的地址,這是不爲C++標準所允許的,和正常的非0的情況也不一致。.NET在這裏採用和c++相同的規則,也認爲空的值類型大小爲1。
最後需要注意的是,如果MyStruct是模板:
1: struct MyStruct
2: {
3: T a;
4: }
如果對Marshal.SizeOf傳入MyStruct<>或者MyStruct這樣的類型,則拋出ArgumentException,因爲Marshal.SizeOf完全不支持泛型。這個是歷史遺留問題,從本質上來講實例化的模板類型(MyStruct)應該是支持的,據說當時主要是沒有時間加上對模板的支持。
同樣的,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')
作者:張羿
轉載請註明出處