在編寫基於.NET架構的應用程序,尤其是分布式時,我們常常極為關注應用程序的執行效率。如果掌握了如何編寫高效率的代碼,就能大幅度地提高應用程序的執行速度,並有助於減少應用程序的瓶頸(bottlenecks)。
18.4.1 撤消
我們知道,類object是.NET架構中的其它一個類的基類。類object是在System中定義的,它並沒有聲明析構函數,而是定義了一個保護類型的成員方法Finalize。
如果.NET的運行時垃圾收集器認為一個對象可以安全從內存中移出時,垃圾收集器就調用該對象的撤消方法Finalize,把對象移出內存,釋放占用的系統資源。我們應該把對一些重要性能的考慮牢記在腦海裡。
如果你有C++的開發背景,你也許會傾向於采用對象的析構函數來撤消對象。不過當你在.NET環境下編程,你就不能再試圖依靠析構函數來執行對象的撤消。
對象的撤消方法Finalize會對程序的性能產生下列不良影響:
自己擁有撤消方法的對象在釋放資源時將耗費更長的時間。
垃圾收集器並不按照一定的順序來撤消對象,也並不保證每一個對象的撤消方法都能被正確的調用。
如果本應該撤消的對象引用了另一個暫時還不能撤消的對象,這個對象也不能撤消。
如果同時有大量的對象在等待撤消,這將會極為耗費系統資源,並降低系統性能。
每個需要清除的對象都必須執行撤消。為了優化性能,在必須使用Finalize方法時,你可以重載一個Close方法,當你需要清除某個對象時,你就可以調用Close方法,從而強迫垃圾收集器調用撤消方法,把該對象設為null。調用GC.SupressFinalize()方法(GC是System中提供的一個類),可以為你的代碼中的元數據設置一個標記,告訴運行時GC不要撤消這個類。這樣,你就可以在對象已沒有用處時立即動手釋放它。
下面的代碼展示了如何設計一個重載Close方法的類:
public class MyClass{ public override void close(){ GC.SupressFinalize(this) } protected override void Finalize(){ Close(); } }
18.4.2 事務
當可操控的代碼必須要和示操控的代碼進行交互時,事務(transition)就發生了。這通常出現在當你需要平台調用服務(Platform Invocation Services,PInvoke)來訪問未操控的動態鏈接庫的靜態指針入口,或者是訪問COM提供的其它方法時。
每一次事務都會帶來少量的開銷,據估計每調用一次事務大約要執行10到40條指令。因此,最好的編程習慣是在代碼中盡量少調用事務。如果情況必須,那就謹慎地使用事務。在使用API函數時,應盡可能地每次執行多個動作,而不是重復調用,但每次只執行少量動作。
18.4.3 值類型
公共語言環境支持兩種類型:值類型和引用類型。值類型表示在內存中占據實際的數據位數,引用類型只表示數據在內存中的位置。我們知道,在運行時,對象類型、接口類型、指針類型都被作為引用類型來對待,而其它的主要類型都被定義為值類型。
究竟使用哪種類型,要看具體情況而定。
在一些情況下,值類型在性能上更有優勢。如果對象被分配GC堆中,這時值類型就被分配在棧中,從而具有更快的運行速度。這是因為它們沒有與類相關聯的開銷,所以不需要調用類的構造函數。另外,值類型的成員通常會被自動初始化為默認值,一般是0或null。你可以通過從System.ValueType中派生來定義自己的值類型。
18.4.4 字符串
字符串在運行時是不可變的。如果我們修改了字符串中的數據,實際上我們不是在修改原來的字符串,而創建了字符串的一個新的實例。為了避免這種情況的發生,我們可以使用the System.Text.Stringbuilder類來創建一個StringBuilder對象,這樣就可以避免創建新的對象實例,而只是修改原來的對象。
在下面的例子中,字符串MyString1和MyString2相連接,實際上我們建立了第三個字符串對象,把連接後的值"Please enter your name"賦予了它。這樣就降低了代碼的性能,因為我們創建了新的對象,分配了新的空間,而已經分配的空間實際上是浪費了。
string MyString1="Please";
string MyString2="enter your name";
MyString1=string Concat(MyString,MyString);
如果我們采用StringBuilder類,就可以解決上面的例子中的性能問題。StringBuilder不會創建新的對象。看改進後的代碼:
StringBuilder MyStringBuilder=new StringBuilder("Please");
String MyNewString="enter your name";
MyStringBuilder.Append(MyNewString);
18.4.5 內聯
內聯表示在編譯時,在對每一個方法的調用處都加上實際的方法代碼,而不是只包含對該方法的引用。這樣做的結果是雖然增加了輸出文件的長度,但是由於減少了對方法調用的開銷,從而加快了程序的運行速度。
聲明內聯的方法是在方法的定義中加上inline關鍵字。
在.NET環境中使用內聯方法可以減少對虛方法的使用,降低系統的開銷。同樣,我們也推薦盡量使用密封類和密封方法。