本文完全獨立於前兩篇文章。如果你喜歡它們,我希望你也會喜歡這個。在上一篇文章中,我展示了哪種方法更快,並比較了代碼的執行速度。在本文中,我將展示不同代碼片段的內存消耗情況。為了顯示內存映射和分配圖,我使用了CLR profiler 32位版本,和往常一樣,我在Windows平台上使用了4GB RAM和Core i3 CPU。內存消耗或分配圖可能根據系統運行的進程而變化。因此,如果你得到一個不同的輸出或行為的代碼,那么請與我們分享你的經驗。
讓我們開始“改進c#代碼的5個技巧:第3部分”的旅程。
StringBuilder消耗的內存比String少
在我的上一篇文章中,我已經展示了在長連接操作的場景中字符串的速度有多慢。這里我們會看到一個字符串和StringBuilder的內存分配圖。讓我來演示一下。下面是我使用字符串和StringBuilder進行相同操作的代碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { public class Test1 { string Name ; public void Process() { Name = Name + "A"; } } public class Test2 { StringBuilder sb = new StringBuilder(); public void Process() { sb.Append("A"); } } class Program { static void Main(string[] args) { Test1 t = new Test1(); t.Process(); Test2 t1 = new Test2(); t1.Process(); } } }
這是代碼執行時的內存分配圖。
這里我們從main函數調用兩個函數Process();盡管它們都有相同的名稱,但它們屬於不同的類和Test1.Process處理字符串數據,而Test2.Process()處理StringBuilder數據。在分配圖中,我們可以看到字符串處理函數消耗了Main()函數94%的資源,而Test2類中處理StringBuilder的Process()只消耗了Main()函數的0.21%的資源。
因此,結論是“當你想多次連接字符串時,總是使用StringBuilder”。
如果可能的話,使用靜態函數
是的,如果可能的話,嘗試實現一個靜態函數,因為靜態對象(函數和數據)不屬於特定類的任何對象。這是大家都有的。因此,如果不創建對象,就不存在內存消耗的問題。下面我將展示一個靜態函數和靜態類的示例。看一下IL代碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { public static class mySclass { public static void Print() { Console.Write("Hello"); } } public class myNclass { public static void Print() { Console.Write("Hello"); } } class Program { static void Main(string[] args) { for (int i = 0; i < 1000; i++) { mySclass.Print(); myNclass.Print(); } } } }
IL代碼在左手邊,在右手邊是由CLR分析器獲取的內存消耗類。由於空間消耗,我無法顯示CLR分析器的完整截圖。但是相信我,靜態類或函數沒有內存分配。
因此,結論是“如果可能,嘗試創建一個靜態函數並使用類名調用,而不是通過對象名調用通用函數”。
字符串格式化VS字符串連接
在第一點中,我展示了字符串如何比StringBuilder消耗更多的資源。在這一點上,我將比較格式化輸出和字符串連接。在第一個函數中,我使用了一個格式規范來打印格式化的輸出(基本上是連接一個字符串)。在另一個函數中,我使用(+)操作符連接一個字符串,如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { class Test { public void Format() { int a = 100; Console.WriteLine("{0}AND{1}", a, a); } public void Concatination() { int a = 100; Console.WriteLine(a + "AND" +a ); } } class Program { static void Main(string[] args) { Test t = new Test(); t.Format(); t.Concatination(); Console.ReadLine(); } } }
在內存分配中我們會看到:
使用format打印字符串的函數消耗了57%的資源,而簡單連接兩個字符串的函數消耗了主函數的30%的資源。因此,我們可以清楚地看到,如果使用字符串連接而不是輸出格式化,就可以節省系統資源。
空程序中哪個類消耗的資源最多?
首先,這一點並不推薦任何最佳實踐技術。我只是想說明,如果我們運行一個空程序(其中只有一個Main()函數),那么分配了多少內存?下面是我的一個非常簡單的程序。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test1 { class Program { static void Main(string[] args) { } } }
是的,我沒有在這個程序里寫任何東西。讓我們看看內存映射。
在這里,我展示了與運行空程序場景相關的六個最耗費資源的類。可以清楚地看到,String類占用了最多的資源(占全部資源的25%)。現在提問。在一個我們從不使用字符串的程序中,為什么字符串類消耗的資源最多?如果我們看一下這個程序的調用圖,我們會看到在main函數中有許多內部函數被調用,它們中的大多數都以字符串作為參數,為了生成這些參數,CLR通常使用string類。如果你有不同的意見,請使用下面的評論框。
實現一個using塊來管理內存
始終實現一個using塊來管理資源是一個最佳實踐。實際上,我們可以證明使用block語句比不使用block語句消耗的內存更少。我們知道,如果我們實現一個using塊的塊代碼大小可能會更大,因為using塊在內部在IL代碼中創建了一個try catch,但一旦它在運行時在IL代碼中實現,它就能有效地處理系統內存。為了演示這一點,我編寫了一個簡單的程序,如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { class Test { public void Test1() { StreamWriter wr = new StreamWriter(@"D:\text.txt"); } public void Test2() { using (StreamWriter wr = new StreamWriter(@"D:\abc.txt")) { } } } class Program { static void Main(string[] args) { Test t = new Test(); t.Test1(); t.Test2(); } } }
在輸出部分,我組合了三個輸出屏幕。
在分配圖中,我們看到using塊比沒有using塊時消耗的資源更少,因為如果我們實現了using塊,程序可以有效地管理內存。
歡迎關注我的公眾號,如果你有喜歡的外文技術文章,可以通過公眾號留言推薦給我。
原文鏈接:https://www.c-sharpcorner.com/UploadFile/dacca2/5-tips-to-improve-your-C-Sharp-code-part-2/