編寫高質量代碼改善程序的157個建議:使用Dynamic來簡化反射的實現

概述

  最近在看《編寫高質量代碼改善C#程序的157個建議》。看到第15個建議的時候,結合平時使用的習慣發現有部分出入,沒有對不對的說法,只是使用習慣有點區別,跟隨着我們來看一看。

  第15條建議是:使用dynamic簡化反射的使用dynamic的確可以簡化反射的使用,但是從性能上來說是有條件的,大家可以根據自己情況選擇。

案例

  原書的案例如下:

static void Main(string[] args)
{
    int times = 1;
    DynamicSample dynamicSample = new DynamicSample();
    var addMethod = typeof(DynamicSample).GetMethod("Add");

    Stopwatch watch1 = Stopwatch.StartNew();
    int result = 0;
    for (int i = 0; i < times; i++)
    {
        result = (int)addMethod.Invoke(dynamicSample, new object[] { 1, 2 });
    }
    Console.WriteLine(string.Format("正常的反射耗時:{0}毫秒", watch1.ElapsedMilliseconds));
    Console.WriteLine("正常反射的結果:" + result);
    /****************************************************************************************************/

    dynamic dynamicSample2 = new DynamicSample();
    int result2 = 0;
    Stopwatch watch2 = Stopwatch.StartNew();
    for (int i = 0; i < times; i++)
    {
        result2 = dynamicSample2.Add(1, 2);
    }
    Console.WriteLine(string.Format("Dynamic的反射耗時:{0}毫秒", watch2.ElapsedMilliseconds));
    Console.WriteLine("Dynamic反射的結果:" + result2);
    /****************************************************************************************************/

    DynamicSample reflectSamplebetter = new DynamicSample();
    var addMethod2 = typeof(DynamicSample).GetMethod("Add");
    var delg = (Func<DynamicSample, int, int, int>)Delegate.CreateDelegate(typeof(Func<DynamicSample, int, int, int>), addMethod2);
    int result3 = 0;
    Stopwatch watch3 = Stopwatch.StartNew();
    for (int i = 0; i < times; i++)
    {
        result3 = delg(reflectSamplebetter, 1, 2);
    }
    Console.WriteLine(string.Format("優化的反射耗時:{0}毫秒", watch3.ElapsedMilliseconds));
    Console.WriteLine("優化的反射結果:" + result3);
    /****************************************************************************************************/

    Console.Read();
}
//測試實體類
public class DynamicSample
{
    public string Name { get; set; }
    public int Add(int a, int b)
    {
        return a + b;
    }
}

執行一次循環的結果如下:

很明顯,正常的反射和優化後的反射要好,dynamic就差點意思了,我們繼續測試。


 執行一萬次循環的結果如下:

執行一萬次循環,優化後的反射依然那麼給力,正常反射的性能也不錯,dynamic就差點意思了。


 我們再測試一下十萬次循環效果怎麼樣,這次測試三次,看看每次的結果。

執行十萬次第 1 次結果如下:

執行十萬次第 2 次結果如下:

執行十萬次第 3 次結果如下:

十萬次測試,總體上來看,性能最好的還是優化後的反射,正常的反射要好一點,dynamic是最差的。


我們最後測試一下一百萬次循環效果怎麼樣,這次測試兩次,看看每次的結果。

執行一百萬次第 1 次結果如下:

執行一百萬次第 2 次結果如下:


  總體上來看,百萬循環,dynamic的好處纔看到,性能也不錯,又簡化反射的使用,性能最好還是優化後的反射,大家明白了吧,不是任何時候dyanmic都是有用的。

總結

  我們都知道反射是有損性能的,無論是使用正常反射還是dynamic都是不好的,儘量不要使用,這是我的建議。

  如果非要用反射,就性能來講,三者之間也是有選擇的,不是說dynamic就是好的,如果就簡化反射來說是好的,但是性能並不是那麼好。

  選擇條件是,反射能不用就不用,如果注重性能,優化後的反射使用方法是最好的,如果不是很複雜,用正常反射就可以,除非循環真的要那麼多次,但是dynamic是真的可以簡化反射使用,對性能是分條件的。

 

正文資料

編寫高質量代碼改善C#程序的157個建議:https://www.cnblogs.com/farmer-y/category/1122939.html

 

157個建議目錄一覽表

第一部分 語言篇

第1章 基本語言要素 / 2

建議1:正確操作字符串 / 2
建議2:使用默認轉型方法 / 6
建議3:區別對待強制轉型與as和is / 9
  能使用as的情況下使用as,性能會優於is。as只能用於引用類型,is則都可以。
建議4:TryParse比Parse好 / 12
  總是使用TryParse,不使用Parse
建議5:使用int?來確保值類型也可以爲null / 15
建議6:區別readonly和const的使用方法 / 16
建議7:將0值作爲枚舉的默認值 / 19
建議8:避免給枚舉類型的元素提供顯式的值 / 20
  個人建議全部顯式賦值,因爲很多時候是將枚舉項的值存在數據庫當中,如果不顯示賦值,那麼當在枚舉項中間插入一個值的時候,後面的值會全部改變。因爲枚舉值默認是+1累進的。
建議9:習慣重載運算符 / 22
建議10:創建對象時需要考慮是否實現比較器 / 23
建議11:區別對待==和Equals / 27
  默認情況下,它們是一樣的;但通常重載Equals()表示“值相等”,==來表示引用相等。
建議12:重寫Equals時也要重寫GetHashCode / 29
建議13:爲類型輸出格式化字符串 / 32
建議14:正確實現淺拷貝和深拷貝 / 36
建議15:使用dynamic來簡化反射實現 / 40
  效率可以提高很多。

第2章 集合和LINQ / 43

建議16:元素數量可變的情況下不應使用數組 / 43
建議17:多數情況下使用foreach進行循環遍歷 / 45
建議18:foreach不能代替for / 51
建議19:使用更有效的對象和集合初始化 / 53
建議20:使用泛型集合代替非泛型集合 / 54
建議21:選擇正確的集合 / 57
建議22:確保集合的線程安全 / 61
建議23:避免將List作爲自定義集合類的基類 / 64
建議24:迭代器應該是隻讀的 / 67
建議25:謹慎集合屬性的可寫操作 / 68
建議26:使用匿名類型存儲LINQ查詢結果 / 70
建議27:在查詢中使用Lambda表達式 / 73
建議28:理解延遲求值和主動求值之間的區別 / 75
建議29:區別LINQ查詢中的IEnumerable和IQueryable / 78
建議30:使用LINQ取代集合中的比較器和迭代器 / 80
建議31:在LINQ查詢中避免不必要的迭代 / 83

第3章 泛型、委託和事件 / 86

建議32:總是優先考慮泛型 / 86
建議33:避免在泛型類型中聲明靜態成員 / 88
建議34:爲泛型參數設定約束 / 90
建議35:使用default爲泛型類型變量指定初始值 / 92
建議36:使用FCL中的委託聲明 / 94
建議37:使用Lambda表達式代替方法和匿名方法 / 96
建議38:小心閉包中的陷阱 / 99
建議39:瞭解委託的實質 / 103
建議40:使用event關鍵字爲委託施加保護 / 106
建議41:實現標準的事件模型 / 108
建議42:使用泛型參數兼容泛型接口的不可變性 / 109
建議43:讓接口中的泛型參數支持協變 / 111
建議44:理解委託中的協變 / 112
建議45:爲泛型類型參數指定逆變 / 114

第4章 資源管理和序列化 / 116

建議46:顯式釋放資源需繼承接口IDisposable / 116
建議47:即使提供了顯式釋放方法,也應該在終結器中提供隱式清理 / 119
建議48:Dispose方法應允許被多次調用 / 120
建議49:在Dispose模式中應提取一個受保護的虛方法 / 121
建議50:在Dispose模式中應區別對待託管資源和非託管資源 / 123
建議51:具有可釋放字段的類型或擁有本機資源的類型應該是可釋放的 / 124
建議52:及時釋放資源 / 125
建議53:必要時應將不再使用的對象引用賦值爲null / 127
建議54:爲無用字段標註不可序列化 / 131
建議55:利用定製特性減少可序列化的字段 / 136
建議56:使用繼承ISerializable接口更靈活地控制序列化過程 / 137
建議57:實現ISerializable的子類型應負責父類的序列化 / 140

第5章 異常與自定義異常 / 144

建議58:用拋出異常代替返回錯誤代碼 / 144
建議59:不要在不恰當的場合下引發異常 / 147
建議60:重新引發異常時使用Inner Exception / 150
建議61:避免在finally內撰寫無效代碼 / 151
建議62:避免嵌套異常 / 157
建議63:避免“喫掉”異常 / 160
建議64:爲循環增加Tester-Doer模式而不是將try-catch置於循環內 / 161
建議65:總是處理未捕獲的異常 / 162
建議66:正確捕獲多線程中的異常 / 166
建議67:慎用自定義異常 / 168
建議68:從System.Exception或其他常見的基本異常中派生異常 / 170
建議69:應使用finally避免資源泄漏 / 172
建議70:避免在調用棧較低的位置記錄異常 / 175

第6章 異步、多線程、任務和並行 / 177

建議71:區分異步和多線程應用場景 / 177
建議72:在線程同步中使用信號量 / 180
建議73:避免鎖定不恰當的同步對象 / 184
建議74:警惕線程的IsBackground / 188
建議75:警惕線程不會立即啓動 / 189
建議76:警惕線程的優先級 / 191
建議77:正確停止線程 / 193
建議78:應避免線程數量過多 / 194
建議79:使用ThreadPool或BackgroundWorker代替Thread / 196
建議80:用Task代替ThreadPool / 198
建議81:使用Parallel簡化同步狀態下Task的使用 / 202
建議82:Parallel簡化但不等同於Task默認行爲 / 204
建議83:小心Parallel中的陷阱 / 205
建議84:使用PLINQ / 208
建議85:Task中的異常處理 / 209
建議86:Parallel中的異常處理 / 214
建議87:區分WPF和WinForm的線程模型 / 216
建議88:並行並不總是速度更快 / 220
建議89:在並行方法體中謹慎使用鎖 / 222

第二部分 架構篇

第7章 成員設計 / 226

建議90:不要爲抽象類提供公開的構造方法 / 226
建議91:可見字段應該重構爲屬性 / 226
建議92:謹慎將數組或集合作爲屬性 / 227
建議93:構造方法應初始化主要屬性和字段 / 228
建議94:區別對待override和new / 229
建議95:避免在構造方法中調用虛成員 / 235
建議96:成員應優先考慮公開基類型或接口 / 236
建議97:優先考慮將基類型或接口作爲參數傳遞 / 237
建議98:用params減少重複參數 / 237
建議99:重寫時不應使用子類參數 / 238
建議100:靜態方法和實例方法沒有區別 / 239
建議101:使用擴展方法,向現有類型“添加”方法 / 240

第8章 類型設計 / 243

建議102:區分接口和抽象類的應用場合 / 243
建議103:區分組合和繼承的應用場合 / 245
建議104:用多態代替條件語句 / 248
建議105:使用私有構造函數強化單例 / 251
建議106:爲靜態類添加靜態構造函數 / 253
建議107:區分靜態類和單例 / 255
建議108:將類型標識爲sealed / 255
建議109:謹慎使用嵌套類 / 256
建議110:用類來代替enum / 257
建議111:避免雙向耦合 / 260
建議112:將現實世界中的對象抽象爲類,將可複用對象圈起來就是命名空間 / 262

第9章 安全性設計 / 264

建議113:聲明變量前考慮最大值 / 264
建議114:MD5不再安全 / 265
建議115:通過HASH來驗證文件是否被篡改 / 268
建議116:避免用非對稱算法加密文件 / 269
建議117:使用SSL確保通信中的數據安全 / 273
建議118:使用SecureString保存密鑰等機密字符串 / 284
建議119:不要使用自己的加密算法 / 289
建議120:爲程序集指定強名稱 / 289
建議121:爲應用程序設定運行權限 / 291

第三部分 編碼規範及習慣

第10章 命名規範 / 296

建議122:以.爲命名空間命名 / 296
建議123:程序集不必與命名空間同名 / 296
建議124:考慮在命名空間中使用複數 / 297
建議125:避免用FCL的類型名稱命名自己的類型 / / 297
建議126:用名詞和名詞組給類型命名 / 298
建議127:用形容詞組給接口命名 / 299
建議128:考慮讓派生類的名字以基類名字作爲後綴 / 300
建議129:泛型類型參數要以T作爲前綴 / 300
建議130:以複數命名枚舉類型,以單數命名枚舉元素 / 301
建議131:用PascalCasing命名公開元素 / 302
建議132:考慮用類名作爲屬性名 / 302
建議133:用camelCasing命名私有字段和局部變量 / 303
建議134:有條件地使用前綴 / 304
建議135: 考慮使用肯定性的短語命名布爾屬性 / 305
建議136:優先使用後綴表示已有類型的新版本 / 306
建議137:委託和事件類型應添加上級後綴 / 307
建議138:事件和委託變量使用動詞或形容詞短語命名 / 308
建議139:事件處理器命名採用組合方式 / 309

第11章 代碼整潔 / 311

建議140:使用默認的訪問修飾符 / 311
建議141:不知道該不該用大括號時,就用 / 312
建議142:總是提供有意義的命名 / 314
建議143:方法抽象級別應在同一層次 / 315
建議144:一個方法只做一件事 / 316
建議145:避免過長的方法和過長的類 / 317
建議146:只對外公佈必要的操作 / 318
建議147:重構多個相關屬性爲一個類 / 319
建議148:不重複代碼 / 320
建議149:使用表驅動法避免過長的if和switch分支 / 321
建議150:使用匿名方法、Lambda表達式代替方法 / 324
建議151:使用事件訪問器替換公開的事件成員變量 / 325
建議152:最少,甚至是不要註釋 / 326
建議153:若拋出異常,則必須要註釋 / 326

第12章 規範開發行爲 / 327

建議154:不要過度設計,在敏捷中體會重構的樂趣 / 327
建議155:隨生產代碼一起提交單元測試代碼 / 336
建議156:利用特性爲應用程序提供多個版本 / 342
建議157:從寫第一個界面開始,就進行自動化測試 / 344

 

參考文獻

 
 

喜歡就點贊加關注。

歡迎關注訂閱微信公衆號【熊澤有話說】,更多好玩易學知識等你來取
作者:熊澤-學習中的苦與樂
公衆號:熊澤有話說

QQ羣:711838388
出處:https://www.cnblogs.com/xiongze520/p/17576853.html
您可以隨意轉載、摘錄,但請在文章內註明作者和原文鏈接。  

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章