改善C#程序,提升程序運行效率的50種方法

1、用屬性代替可訪問的字段算法

  一、.NET數據綁定只支持數據綁定,使用屬性能夠得到數據綁定的好處;編程

  二、在屬性的get和set訪問器重可以使用lock添加多線程的支持。數組

  2、readonly(運行時常量)和const(編譯時常量)

  一、const只可用於基元類型、枚舉、字符串,而readonly則能夠是任何的類型;安全

  二、const在編譯時將替換成具體的常量,這樣若是在引用中同時使用了const和readonly兩種值,則對readonly的再次改變將會改變設計的初衷,這是須要從新編譯所更改的程序集,以從新引用新的常量值。服務器

  三、const比readonly效率高,但失去了應用的靈活性。多線程

  3、is與as

  一、二者都是在運行時進行類型的轉換,as操做符只能使用在引用類型,而is可使用值和引用類型;框架

  二、一般的作法是用is判斷類型,而後選擇使用as或強類型轉換操做符(用operater定義的轉換)有選擇地進行。ide

  4、ConditionalAttribute代替#if #endif條件編譯

  一、ConditionalAttribute只用於方法級,對其餘的如類型、屬性等的添加都是無效的;而#if #endif則不受此限制;函數

  二、ConditionalAttribute能夠添加多個編譯條件的或(OR)操做,而#if #endif則能夠添加與(AND)[這裏能夠徹底定義爲另外一個單獨的符號];工具

  三、ConditioanlAttribute定義能夠放在一個單獨的方法中,使得程序更爲靈活。

  5、提供ToString()方法

  一、能夠更友好的方式提供用戶詳細的信息;

  二、使用IFormatter.ToString()方法提供更靈活的定製,若是添加IFormatProvider 和ICustomFormatter接口則更有意義的定製消息輸出。

  6、值和引用類型的區別

  一、值類型不支持多態,適合存儲應用程序操做的數據,而引用則支持多態,適用於定義應用程序的行爲;

  二、對於數組定義爲值類型能夠顯著提升程序的性能;

  三、值類型具備較少的堆內存碎片、內存垃圾和間接訪問時間,其在方法中的返回是以複製的方式進行,避免暴露內部結構到外界;

  四、值類型應用在以下的場景中:類型的職責主要是用於數據存儲;公共接口徹底由一些數據成員存取屬性定義;永遠沒有子類;永遠沒有多態行爲。

  7、值類型儘量實現爲常量性和原子性的類型

  一、使咱們的代碼更易於編寫和維護;

  二、初始化常量的三種策略:在構造中;工廠方法;構造一個可變的輔助類(如StringBuilder)。

  8、確保0爲值得有效狀態

  一、值類型的默認狀態應爲0;

  二、枚舉類型的0不該爲無效的狀態;在FlagsAttribute是應確保0值爲有效地狀態;

  三、在字符串爲爲空時能夠返回一個string.Empty的空字符串;

  9、相等判斷的多種表示關係

  一、ReferenceEquals()判斷引用相等,須要兩個是引用同一個對象時方可返回true;

  二、靜態的Equals()方法先進性引用判斷再進行值類型判斷的;

  三、對於引用類型的判斷能夠在使用值語義時使用重寫Equals()方法;

  四、重寫Equals()方法時也應當重寫GetHashCode()方法,同時提供operater==()操做。

  10、理解GetHashCode()方法的缺陷

  一、GetHashCode()僅應用在基於散列的集合定義鍵的散列值,如HashTable或Dictionary;

  二、GetHashCode()應當遵循相應的三條規則:兩個相等對象應當返回相同的散列碼;應當是一個實例不變式;散列函數應該在全部的整數中產生一個隨機的分佈;

  11、優先使用foreach循環語句

  一、foreach能夠消除編譯器對for循環對數組邊界的檢查;

  二、foreach的循環變量是隻讀的,且存在一個顯式的轉換,在集合對象的對象類型不正確時拋出異常;

  三、foreach使用的集合須要有:具有公有的GetEnumberator()方法;顯式實現了IEnumberable接口;實現了IEnumerator接口;

  四、foreach能夠帶來資源管理的好處,由於若是編譯器能夠肯定IDisposable接口時可使用優化的try…finally塊;

  12、默認字段的初始化優於賦值語句

  一、字段生命默認會將值類型初始化爲0,引用類型初始化爲null;

  二、對同一個對象進行屢次初始化會下降代碼的執行效率;

  三、將字段的初始化放到構造器中有利於進行異常處理。

  十3、使用靜態構造器初始化靜態成員

  一、靜態構造器會在一個類的任何方法、變量或者屬性訪問以前執行;

  二、靜態字段一樣會在靜態構造器以前運行,同時靜態構造器有利於異常處理。

  十4、利用構造器鏈(在.NET 4.0已經用可選參數解決了這個問題)

  一、用this將初始化工做交給另外一個構造器,用base調用基類的構造器;

  二、類型實例的操做順序是:將全部的靜態字段都設置爲0;執行靜態字段初始化器;執行基類的靜態構造器;執行當前類型的靜態構造器;

  將全部的實例字段設置爲0;執行實例字段初始化器;執行合適的基類實例構造器;執行當前類型的實例構造器。

  十5、利用using和try/finally語句來清理資源

  在IDisposable接口的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器再也不執行終結操做。

  十6、儘可能減小內存垃圾

  一、分配和銷燬一個對上的對象都要花費額外的處理器時間;

  二、減小分配對象數量的技巧:常用的局部變量提高爲字段;提供一個類,用於存儲Singleton對象來表達特定類型的經常使用實例。

  三、用StringBuilder進行復雜的字符串操做。

  十7、儘可能減小裝箱和拆箱

  一、關注一個類型到System.Object的隱式轉換,同時值類型不該該被替換爲System.Object類型;

  二、使用接口而不是使用類型能夠避免裝箱,即將值類型從接口實現,而後經過接口調用成員。

  十8、實現標準Dispose模式

  一、使用非內存資源,它必須有一個終結器,垃圾收集器在完成沒有終結其的內存對象後會將實現了終結器對象的添加到終結隊列中,而後垃圾收集器會啓動一個新的線程來運行這些對象上的終結器,這種防護性的變成方式是由於若是用戶忘記了調用Dispose()方法,垃圾回收器老是會調用終結器方法的,這樣能夠避免出現非託管的內存資源不被釋放引發內存泄漏的問題;

  二、使用IDisposable.Dispose()方法須要作四個方面的工做:釋放全部的非託管資源;釋放全部的託管資源;設置一個狀態標記來表示是否已經執行了Dispose();調用GC.SuppressFinalize(this)取消對象的終結操做;

  三、爲須要多態的類型添加一個受保護的虛方法Dispose(),派生類經過重寫這個方法來釋放本身的任務;

  四、在須要IDisoposable接口的類型中,即便咱們不須要一個終結器也應該實現一個終結器。

  十9、定義並實現接口優於繼承類型

  一、不相關的類型能夠共同實現一個共同的接口,並且實現接口比繼承更容易;

  二、接口比較穩定,他將一組功能封裝在一個接口中,做爲其餘類型的實現合同,而基類則能夠隨着時間的推移進行擴展。

  二10、明辨接口實現和虛方法重寫

  一、在基類中實現一個接口時,派生類須要使用new來隱藏對基類方法的使用;

  二、能夠將基類接口的方法申明爲虛方法,而後再派生類中實現。

  二11、使用委託表達回調

  一、委託對象自己不提供任何異常捕獲,因此任何的多播委託調用都會結束整個調用鏈;

  二、經過顯示調用委託鏈上的每一個委託目標能夠避免多播委託僅返回最後一個委託的輸出。

  二12、使用事件定義外部接口

  一、應當聲明爲共有的事件,讓編譯器爲咱們建立add和renmove方法;

  二、使用System.ComponentModel.EventHandlerList容器來存儲各個事件處理器,在類型中包含大量事件時可使用他來隱藏全部事件的複雜性。

  二十3、避免返回內部類對象的引用

  一、因爲值類型對象的訪問會建立一個該對象的副本,因此定義一個值類型的的屬性徹底不會改變類型對象內部的狀態;

  二、常量類型能夠避免改變對象的狀態;

  三、定義接口將訪問限制在一個子集中從而最小化對對象內部狀態的破壞;

  四、定義一個包裝器對象來限制另外一個對象的訪問;

  五、但願客戶代碼更改內部數據元素時能夠實現Observer模式,以使對象能夠對更改進行校驗或相應。

  二十4、聲明式編程優於命令式編程

  能夠避免在多個相似的手工編寫的算法中犯錯誤的可能性,並提供清晰和可讀的代碼。

  二十5、儘量將類型實現爲可序列化的類型

  一、類型表示的不是UI控件、窗口或者表單,都應使類型支持序列化;

  二、在添加了NonSerializedAttribute的反序列化的屬性時能夠經過實現IDeserializationCallback的OnDeserialization()方法裝入默認值;

  三、在版本控制中可使用ISerializable接口來進行靈活的控制,同時提供一個序列化的構造器來根據流中的數據初始化對象,在實現時還要求SerializationFormatter異常的許可。

  四、若是須要建立派生類則須要提供一個掛鉤方法供派生類使用。

  二十6、使用IComparable和IComparer接口實現排序關係

  一、IComparable接口用於爲類型實現最天然的排序關係,重載四個比較操做符,能夠提供一個重載版的CompareTo()方法,讓其接受具體類型做爲參數;

  二、IComparer用於提供有別於IComparable的排序關係,或者爲咱們提供類型自己說沒有實現的排序關係。

  二十7、避免ICloneable接口

  一、對於值類型永遠不須要支持ICloneable接口使用默認的賦值操做便可;

  二、對於可能須要支持ICloneable接口的基類,應該爲其創造一個受保護的複製構造器,並應當避免支持IConeable接口。

  二十8、避免強制轉換操做符

  經過使用構造器來代替轉換操做符可使轉換工做變得更清晰,因爲在轉換後使用的臨時對象,容易致使一些詭異的BUG。

  二十9、只有當新版積累致使問題是才考慮使用new修飾符

  三10、儘量實現CLS兼容的程序集

  一、建立一個兼容的程序集須要遵循兩條規則:程序集中全部公有和受保護成員所使用的參數和返回值類型都必須與CLS兼容;任何與CLS不兼容的公有和受保護成員都必須有一個與CLS兼容的替代品;

  二、能夠經過顯式實現接口來避開CLS兼容類型檢查,及CLSCompliantAttribute不會檢查私有的成員的CLS兼容性。

  三11、儘量實現短小簡潔的方法

  一、JIT編譯器以方法爲單位進行編譯,沒有被調用的方法不會被JIT編譯;

  二、若是將較長的Switch中的Case語句的代碼替換成一個一個的方法,則JIT編譯器所節省的時間將成倍增長;

  三、短小精悍的方法並選擇較少的局部變量能夠得到優化的寄存器使用;

  四、方法內的控制分支越少,JIT編譯器越容易將變量放入寄存器。

  三12、儘量實現小尺寸、高內聚的程序集

  一、將全部的公有類以及共用的基類放到一些程序集中,把爲公有類提供功能的工具類也放入一樣的程序集中,把相關的公有接口打包到他們本身的程序集中,最後處理遍及應用程序中水平位置的類;

  二、原則上建立兩種組件:一種爲小而聚合、具備某項特定功能的程序集,另外一種爲大而寬、包含共用功能的程序集。

  三十3、限制類型的可見性

  一、使用接口來暴露類型的功能,可使咱們更方便地建立內部類,同時又不會限制他們在程序集外的可用性;

  二、向外暴露的公有類型越少,將來擴展和更改實現所擁有的選擇就越多。

  三十4、建立大粒度的Web API

  這是在機器之間的交易的頻率和載荷都降到最低,將大的操做和細粒度的執行放到服務器執行。

  三十5、重寫優於事件處理器

  一、一個事件處理器拋出異常,則事件鏈上的其餘處理器將不會被調用,而重寫的虛方法則不會出現這種狀況;

  二、重寫要比關聯事件處理器高效得多,事件處理器須要迭代整個請求列表,這樣佔用了更多的CPU時間;

  三、事件能在運行時響應,具備更多的靈活性,能夠對同一個事件關聯多個響應;

  四、通行的規則是處理一個派生類的事件是,重寫方式較好。

  三十6、合理使用.NET運行時診斷

  一、System.Diagnostics.DebugTraceEventLog爲運行時提供了程序添加診斷信息所須要的全部工具,EventLog提供入口時的應用程序能寫到系統事件日誌中;

  二、最後不要寫本身的診斷庫,.NET FCL 已經擁有了咱們須要的核心庫。

  三十7、使用標準配置機制

  一、.NET框架的System.Windows.Application類爲咱們定義了創建通用配置路徑的屬性;

  二、Application.LocalAppDataPath和Application.userDataPath 會生成本地數據目錄和用戶數據的路徑名;

  三、不要在ProgramFiles和Windows系統目錄中寫入數據,這些位置須要更高的安全權限,不要期望用戶擁有寫入的權限。

  三十8、定製和支持數據綁定

  一、BindingMananger和CurrencyManager這兩個對象實現了控件和數據源之間的數據傳輸;

  二、數據綁定的優點:使用數據綁定要比編寫本身的代碼簡單得多;應該將它用於文本數據項以外的範圍-其餘顯示屬性也能夠被綁定;對於Windowos Forms 數據綁定可以處理多個控件同步的檢查相關數據源;

  三、在對象不支持所需的屬性時能夠經過屏蔽當前的對象而後添加一個想要的對象來支持數據綁定。

  三十9、使用.NET驗證

  一、ASP.NET中有五種控件來驗證有效性,能夠用CustomValidator派生一個新類來增長本身的認證器;

  二、Windows驗證須要子System.Windows.Forms.Control.Validating些一個事件處理器。

  四10、根據須要選用恰當的集合

  一、數組有兩個比較明顯的缺陷:不能動態的調整大小;調整大小很是耗時;

  二、ArrayList混合了一維數組和鏈表的特徵,Queue和Stack是創建在Array基礎上的特殊數組;

  三、當程序更加靈活的添加和刪除項時,可使更加健壯的集合類型,當建立一個模擬集合的類時,應當爲其實現索引器和IEnumberable接口。

  四11、DataSet優於自定義結構

  一、DataSet有兩個缺點個:使用XML序列化機制的DataSet與非.NET 代碼之間的交互不是很好;DataSet是一個很是通用的容器;

  二、強類型的DataSet打破了更多的設計規則,其得到的開發效率要遠遠高於本身編寫的看上去更爲優雅的設計。

  四12、利用特性簡化反射

  經過設計和實現特性類,強制開發人員用他們來聲明可被動態使用的類型、方法和屬性,能夠減小應用程序的運行時錯誤,提升軟件的用戶滿意度。

  四十3、避免過分使用反射

  一、Invoke成員使用的參數和返回值都是System.Object,在運行時進行類型的轉換,但出現問題的可能性也變得更多了;

  二、接口使咱們能夠獲得一個更爲清晰、也更具可維護性的系統,反射式一個很強大的晚期綁定機制.NET框架使用它來實現Windows控件和Web控件的數據綁定。

  四十4、爲應用程序建立特定的異常類

  一、須要不一樣的異常類的惟一緣由是讓用戶在編寫catch處理器時可以方便地對不一樣的錯誤採起不一樣的作法;

  二、可能有不一樣的修復行爲時咱們才應該建立多種不一樣的異常類,經過提供異常基類所支持的全部構造器,能夠爲應用程序建立功能完整的異常類,使用InnerException屬性能夠保存更低級別錯誤條件所產生的全部錯誤信息。

  四十5、優先選擇異常安全保證

  一、強異常保證在從異常中恢復和簡化異常處理之間提供了最好的平衡,在操做由於異常而中斷,程序的狀態保留不變;

  二、對將要修改的數據作防護性的複製,對這些數據的防護性複製進行修改,這中間的操做可能會引起異常,將臨時的副本和原對象進行交換;

  三、終結器、Dispose()方法和委託對象所綁定的目標方法在任何狀況下都應當確保他們不會拋出異常。

  四十6、最小化互操做

  一、互操做有三個方面的代價:數據在託管堆和非託管堆之間的列舉成本,託管代碼和非託管代碼之間切換的成本,對開發人員來講與混合環境打交道的開發工做;

  二、在interop中使用blittable類型能夠有效地在託管和非託管環境中來回複製,而不受對象內部結構的影響;

  三、使用In/Out特性來確保最貼切的沒必要要的屢次複製,經過聲明數據如何被列舉來提升性能;

  四、使用COM Interop用最簡單的方式實現和COM組件的互操做,使用P/Invoke調用Win32 API,或者使用C++編譯器的/CLR開關來混合託管和非託管的代碼;

  四十7、優先選擇安全代碼

  一、儘量的避免訪問非託管內存,隔離存儲不能防止來自託管代碼和受信用戶的訪問;

  二、程序集在Web上運行時能夠考慮使用隔離存儲,當某些算法確實須要更高的安全許可時,應該將那些代碼隔離在一個單獨的程序集中。

  四十8、掌握相關工具與資源

  一、使用NUnit創建自動單元測試(集成在VS2010 中了);

  二、FXCop工具會獲取程序集中的IL代碼,並將其與異族編碼規則和最佳實踐對照分析,最後報告違例狀況;

  三、ILDasm是一個IL反彙編工具,能夠幫助咱們洞察細節;

  四、Shared Source CLI是一個包含.NET框架內核和C#編譯器的實現源碼。

  四十9、爲C#2.0作準備(這個規則如今已經沒什麼意義了,畢竟如今已經到了4.0 )

  五10、瞭解ECMA標準

 

1.foreach VS for 語句

Foreach 要比for具備更好的執行效率Foreach的平均花費時間只有for的30%.經過測試結果在for和foreach均可以使用的狀況下,咱們推薦使用效率更高的foreach另外,用for寫入數據時間大約是讀取數據時間的10倍左右.

2.避免使用ArrayList

ArrayList的性能低下任何對象添加到ArrayList中都要封箱爲System.Object,從ArrayList中取出數據都要拆箱回實際的類型 泛型集合類的高性能,泛型集合是強類型的

3.使用HashTable字典集合   

  當存放少許數據時建議使用HashTable取代像StringDictionary,NameValueCollection, HybridCollection這樣的字典集合

4.爲字符串容器聲明常量   

爲字符串容器聲明常量,不要直接把字符串封裝在雙引號」」中,避免字符串對象不斷在內存中建立和釋放.提升字符串對象的訪問效率.

//避免MyObject  obj = new MyObject();obj.Status = 「Active」;

//推薦const  string c = 「Acive」;MyObject  obj = new MyObject();obj.Status = c;

5.使用String.Compare()字符串比較不要使用UpperCase或LowerCase轉換字符串的大小寫,再進行比較使用String.Compare()可忽略字符串大小寫進行比較

String  strTemp = 「Active」;If(String.Compare(strTemp,」active」,true)==0){         Consolt.Write(「Equal」);}

6.使用StringBuilder字符串拼接1.String類對象是不可變的(只讀),

對於String對象的從新賦值,本質是從新建立一個新的String對象並賦予新值給該對象.2.System.Text.StringBuilder 維護一個長度等於Capacity的字符串(能夠看做字符數組),當Capacity長度的字符串不足以容納結果字符串時,StringBuilder開闢新的長度爲通過上面的規則計算好的Capacity的內存區域,將原字符串複製到新的內存區域再進行操做,原字符串區域交給GC回收。所以這裏也涉及到內存的分配與回收,使用StringBuilder時最好估算一下所需容量,用這個容量初始化Capacity,提升性能。StringBuilder不能保證全部實例成員都是線程安全的,儘管在類型定義中加入了不少線程安全的控制,若是要確保其線程安全,須手工實現線程同步機制。

7.XPathDocument讀取XML文件若是隻是讀取XML對象的數據,那麼用只讀的XPathDocument代替XMLDocument,能夠提升性能

8.避免在循環體裏聲明變量,

應該在循環體外聲明變量,在循環體內初始化變量

//避免For(int i=0;i<10;i++){       SomeClass obj = new SomeClass();       //…}

//推薦SomeClass obj = null;For(int i=0; i<10; i++){       obj = new SomeClass();       //…}

9.捕獲指定的異常捕獲異常時,

應使用具體的異常類進行捕獲,並按照異常所捕得到範圍按照由小到大的順序進行定義不要使用通用的

System.ExceptionPrivate void Find(object obj){  

     try{            

     Console.write(obj.ToString());    

       }     

  catch(ArgumentNullException ane)         

  {    //…    }     

 catch(ArgumentException ae)    

       {    //…    }      

catch(SystemException se)      

     {   //…     }       catch(Exception e)      

     {   //…     }  }不要使用Exception控制流程,捕獲異常對性能的損耗是衆所周知的.所以最好可以避免異常的發生

10.使用using和try/finally清理資源.NET 平臺在內存管理方面提供了GC(Garbage Collection)

負責自動釋放託管資源和內存回收的工做,但它沒法對非託管資源進行釋放,這時咱們必須本身提供方法來釋放對象內分配的非託管資源使用非託管資源的類型必須實現IDisposable接口的Dispose方法來精確的釋放資料當使用帶有Dispose方法的類型化資源時,應在使用完畢時調用Dispose方法,及時釋放掉不用的資源.使用using或try/finally能更好的保證Dispose方法被及時的調用

11.避免濫用反射反射是比較浪費性能的操做,

應避免濫用反射影響性能的緣由:當使用反射來調用類型或觸發方法,訪問字段屬性時,CLR須要作更多的工做:校驗參數,檢查權限等.當編寫一個動態構造類型(晚綁定)的應用時,可採起如下方式進行替換經過類的繼承關係經過接口實現經過委託實現

12.避免裝箱操做使用值類型的ToString方法避免裝箱操做緣由:

數字和字符串拼接的時候,由於數據類型不一樣,數字經過裝箱操做轉換爲引用類型後才能與字符串進行拼接.

//建議int num=5;string str = 「link me」+num.ToString();

13.HttpServerUtility.Transfer 採用 Server.Transfer 語法

在頁面中使用該方法可避免沒必要要的客戶端重定向(Response.Redirect)。

Int32.TryParse()類型轉化Int32.TryParse()優於Int32.Parse()優於Convert.ToInt32()

Convert.ToInt32 會把最終的解析工做代理給Int32.Parse

Int32.Parse 會把最終的解析工做代理給Number.ParseInt32

Int32.TryParse 會把最終的解析工做代理給Number.TryParseInt32

相關文章
相關標籤/搜索