C++模版和C#泛型求同存异录(一)sizeof(T)
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
ret
} // 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 asan 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,歡迎加我微信。
如需轉發文章請加微信號獲取轉發授權
總結
以上是生活随笔為你收集整理的C++模版和C#泛型求同存异录(一)sizeof(T)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF Core 3.0查询
- 下一篇: .NET Core 3.0之深入源码理解