我們知道 C#的語法與C++非常相似實現從C++向C#的轉變其困難不在於語言本身而在於熟悉NET的可管理環境和對NET框架的理解
盡管C#與C++在語法上的變化是很小的幾乎不會對我們有什麼影響但有些變化卻足以使一些粗心的C++編程人員時刻銘記在心在本篇文章中我們將討論C++編程人員最容易犯的十個錯誤
陷阱 沒有明確的結束方法
幾乎可以完全肯定地說對於大多數C++編程人員而言C#與C++最大的不同之處就在於碎片收集這也意味著編程人員再也無需擔心內存洩露和確保刪除所有沒有用的指針但我們再也無法精確地控制殺死無用的對象這個過程事實上在C#中沒有明確的destructor
如果使用非可管理性資源在不使用這些資源後必須明確地釋放它對資源的隱性控制是由Finalize方法(也被稱為finalizer)提供的當對象被銷毀時它就會被碎片收集程序調用收回對象所占用的資源
finalizer應該只釋放被銷毀對象占用的非可管理性資源而不應牽涉到其他對象如果在程序中只使用了可管理性資源那就無需也不應當執行Finalize方法只有在非可管理性資源的處理中才會用到Finalize方法由於finalizer需要占用一定的資源因此應當只在需要它的方法中執行finalizer
直接調用一個對象的Finalize方法是絕對不允許的(除非是在子類的Finalize中調用基礎類的Finalize)碎片收集程序會自動地調用Finalize
從語法上看C#中的destructor與C++非常相似但其實它們是完全不同的C#中的destructor只是定義Finalize方法的捷徑因此下面的二段代碼是有區別的
~MyClass()
{
// 需要完成的任務
}
MyClassFinalize()
{
// 需要完成的任務
baseFinalize();
}
錯誤Finalize和Dispose使用誰?
從上面的論述中我們已經很清楚顯性地調用finalizer是不允許的它只能被碎片收集程序調用如果希望盡快地釋放一些不再使用的數量有限的非可管理性資源(如文件句柄)則應該使用IDisposable界面這一界面有個Dispose方法它能夠幫你完成這個任務Dispose是無需等待Finalize被調用而能夠釋放非可管理性資源的方法
如果已經使用了Dispose方法則應當阻止碎片收集程序再對相應的對象執行Finalize方法為此需要調用靜態方法GCSuppressFinalize並將相應對象的指針傳遞給它作為參數Finalize方法就能調用Dispose方法了據此我們能夠得到如下的代碼
public void Dispose()
{
// 完成清理操作
// 通知GC不要再調用Finalize方法
GCSuppressFinalize(this);
}
public override void Finalize()
{
Dispose();
baseFinalize();
}
對於有些對象可能調用Close方法就更合適(例如對於文件對象調用Close就比Dispose更合適)可以通過創建一個private屬性的Dispose方法和public屬性的Close方法並讓Close調用Dispose來實現對某些對象調用Close方法
由於不能確定一定會調用Dispose而且finalizer的執行也是不確定的(我們無法控制GC會在何時運行)C#提供了一個Using語句來保證Dispose方法會在盡可能早的時間被調用一般的方法是定義使用哪個對象然後用括號為這些對象指定一個活動的范圍當遇到最內層的括號時Dispose方法就會被自動調用對該對象進行處理
using SystemDrawing;
class Tester
{
public static void Main()
{
using (Font theFont = new Font(Arial f))
{
//使用theFont對象
} // 編譯器將調用Dispose處理theFont對象
Font anotherFont = new Font(Courierf);
using (anotherFont)
{
// 使用anotherFont對象
} // 編譯器將調用Dispose處理anotherFont對象
}
}
在本例的第一部分中Font對象是在Using語句中創建的當Using語句結束時系統就會調用Dispose對Font對象進行處理在本例的第二部分Font對象是在Using語句外部創建的在決定使用它時再將它放在Using語句內當Using語句結束時系統就會調用Dispose
Using語句還能防止其他意外的發生保證系統一定會調用Dispose
錯誤C#中的值型變量和引用型變量是有區別的
與C++一樣C#也是一種強類型編程語言C#中的數據類型被分為了二大類C#語言本身所固有的數據類型和用戶自定義數據類型這一點也與C++相似
此外C#語言還把變量分為值類型和引用類型除非是被包含在一個引用類型中值類型變量的值保留在棧中這一點與C++中的變量非常相似引用類型的變量也是棧的一種它的值是堆中對象的地址與C++中的指針非常地相似值類型變量的值被直接傳遞給方法引用型變量在被作為參數傳遞給方法時傳遞的是索引
類和界面可以創建引用類變量但需要指出的是結構數據類型是C#的一種內置數據類型同時也是一種值型的數據類型
錯誤注意隱性的數據類型轉換
Boxing和unboxing是使值型數據類型被當作索引型數據類型使用的二個過程值型變量可以被包裝進一個對象中然後再被解包回值型變量包括內置數據類型在內的所有C#中的數據類型都可以被隱性地轉化為一個對象包裝一個值型變量就會生成一個對象的實例然後將變量拷貝到實例中
Boxing是隱性的如果在需要索引型數據類型的地方使用了值型數據類型的變量值型變量就會隱性地轉化為索引型數據類型的變量Boxing會影響代碼執行的性能因此應當盡量避免尤其是在數據量較大的時候
如果要將一個打包的對象轉換回原來的值型變量必須顯性地對它進行解包解包需要二個步驟首先對對象實例進行檢查確保它們是由值型的變量被包裝成的第二步將實例中的值拷貝到值型變量中為了確保解包成功被解包的對象必須是通過打包一個值型變量的值生成的對象的索引
using System;
public class UnboxingTest
{
public static void Main()
{
int i = ;
//打包
object o = i;
// 解包(必須是顯性的)
int j = (int) o;
ConsoleWriteLine(j: {} j);
}
}
如果被解包的對象是無效的或是一個不同數據類型對象的索引就會產生InvalidCastException異外
錯誤結構與對象是有區別的
C++中的結構與類差不多唯一的區別是在缺省狀態下結構的訪問權限是public其繼承權限也是public一些C++編程人員將結構作為數據對象但這只是一個約定而非是必須這樣的
在C#中結構只是一個用戶自定義的數據類型並不能取代類盡管結構也支持屬性方法域和操作符但不支持繼承和destructor
更重要的是類是一種索引型數據類型結構是值型數據類型因此結構在表達無需索引操作的對象方面更有用結構在數組操作方面的效率更高而在集合的操作方面則效率較低集合需要索引結構必須打包才適合在集合的操作中使用類在較大規模的集合操作中的效率更高
From:http://tw.wingwit.com/Article/program/net/201311/14709.html