Marshal.SizeOf和sizeof的區別

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')


作者:張羿
轉載請註明出處

發佈了119 篇原創文章 · 獲贊 3 · 訪問量 78萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章