天天看點

.NET 托管、非托管、本地:這些代碼有什麼差別?

http://www.codeguru.com/Csharp/.NET/cpp_managed/article.php/c4871

本文内容

  • 什麼是托管代碼?
  • 什麼是非托管代碼?
  • 什麼是本地代碼?
  • 托管代碼意味着托管資料?

雖然該文章寫的時間比較早,但是完全可以說明問題。

2003 年 4 月 24 日 VS 2003(之前稱為 Everett)釋出,很多開發者願意考慮使用托管代碼的新技術。但是對這個新技術有點迷茫,特别是 C++ 開發者。因為,正如我之前指出的,C++ 是特殊的。

什麼是托管代碼(Managed Code)?

托管代碼是 Visual Basic .NET 和 C# 編譯器建立。它們編譯成中間語言(IL),而不是直接在計算機上運作的機器代碼。IL 被儲存在一個稱為程式集中,以及描述代碼的類、方法、和屬性(如安全性要求)的中繼資料檔案。在 .NET 裡,程式集是“一站式(one-stop-shopping)”部署的單元。可以将程式集直接複制到另一台伺服器上進行部署。

托管代碼運作在公共語言運作庫(Common Language Runtime,CLR)。CLR 為正在運作的代碼提供多種服務。在通常代碼事件中,它首先加載和驗證程式集,確定 IL 正确。然後,當程式集運作,方法被調用時,CLR 安排它們被編譯為适合機器的機器碼,并緩存該機器碼,以便下次被調用時使用。這被稱為 Just In Time,或 JIT compiling,或簡稱 Jitting。

當程式集運作時,CLR 繼續提供諸如安全、記憶體管理、線程和其他的服務。應用程式是由 CLR 來管理的。

Visual Basic .NET 和 C# 隻能産生托管代碼。如果你用的是這些應用程式,那麼你就正在使用托管代碼。Visual C ++ .NET 也能産生托管代碼,就是當你建立一個項目,選擇名為“.Managed”開始的應用程式類型,如“.Managed C++ application”。

什麼是非托管代碼(Unmanaged Code)?

非托管代碼是你以前用 Visual Studio .NET 2002 建立的代碼。現在你還有很多 Visual Basic 6、Visual C ++ 6,甚至是使用了 15 年的老 C 編譯器産生的非托管代碼。非托管代碼直接被編譯成機器碼,跟你具有相同或幾乎相同的晶片的機器上都能運作。但是,它不能從不可見的運作時裡獲得諸如安全或記憶體管理等服務,而隻能從作業系統獲得,通過調用 Windows SDK 提供的 API。最近,有越來越多的非托管應用程式通過 COM 調用獲得作業系統的服務。

不同于 Visual Studio 2003 中的其他語言,Visual C ++ 可以建立非托管應用程式。當你建立一個項目,選擇以 MFC、ATL 或 Win32 開頭的應用程式,就建立了一個非托管的應用程式。

這導緻一些混淆,也就是說,當建立一個托管的 C++ 應用程式時,那麼生成結果是一個 .exe 擴充名的 IL 裝配檔案。當你建立一個 MFC 應用程式,生成結果是一個本地代碼(native code)的 Windows 可執行檔案,擴充名也是 .exe。這兩個檔案的内部布局完全不同。你可以使用中間語言反彙編工具(Intermediate Language Disassembler)ildasm,檢視内部裝配,以及中繼資料檔案和 IL。在非托管的 .exe 檔案使用 ildasm,你會被告知沒有有效的 CLR 頭,不能反彙編——雖然擴充名相同,但檔案完全不同。

什麼是本地代碼(Native Code)?

本地代碼在兩種情況下使用。許多人把非托管代碼作為本地代碼的同義詞:用舊的編譯工具或是 Visual C ++ 生成代碼,不會運作在 CLR 上,而是在計算機本地。這可能是一個完整的應用,也可能是一個 COM 元件或通過 COM Interop 或 PInvoke 由托管代碼調用的 DLL。COM Interop 和 PInvoke 是強大的工具,確定,當你将舊代碼移動到新環境時,仍然可以使用。我更喜歡說非托管代碼。從這個意義上,因為它強調的是代碼不會得到運作時的服務。例如,托管代碼中代碼通路安全性(Code Access Security),會阻止代碼從另一個服務裝載代碼,防止破壞性的行為。如果你的應用程式從另一個服務加載非托管代碼,就無法得到這種保護。

本地代碼的另一個使用是描述了 JIT 編譯器的輸出,機器代碼事實上是運作在運作時上。這是托管的,不是 IL,而是機器碼。是以,不能假設本地代碼等同于非托管代碼。

對于 Visual Basic 和 C#,生存期問題很簡單,因為你别無選擇。當你聲明一個類,在托管堆上建立該類的執行個體,而垃圾回收器關心該執行個體的生存期問題。但是在 Visual C++,你是有選擇的。即使你建立一個托管應用程式,也可以由你決定類是否是一個托管類型或一個非托管類型。

下面是一個非托管類型:

class Foo      
{      
private:      
int x;      
public:      
Foo(): x(0){}      
Foo(int xx): x(xx) {}      
};      

下面是一個托管類型:

__gc class Bar      
{      
private:      
int x;      
public:      
Bar(): x(0){}      
Bar(int xx): x(xx) {}      
};      

唯一的差別是Bar定義前邊的 __gc 關鍵字,但卻有很大不同。關于 __gc 關鍵字參見MSDN:ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_fxintro/html/6a1f9b6c-9983-4722-8b58-13e9af2a1339.htm

托管類型是可以被回收的。它們必須用new來建立,不在堆上。是以,下面是對的:

Foo f;      

但下面是不允許的:

Bar b;      

如果我必須在堆上建立Foo的一個執行個體,那一定要記着清除它:

Foo* pf = new Foo(2);      
// . . .      
delete pf;      

在C ++編譯器實際上使用兩個堆,一個是托管的,一個是非托管的,使用重載的new操作符,當建立用一個new建立執行個體時,來确定配置設定的記憶體在哪裡。如果在堆上建立Bar的一個執行個體,那麼可以忽略它。垃圾回收器将會回收它。在 Visual C++,你是有選擇的。

繼續閱讀