程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 關於優化C#程序的五十種方法

關於優化C#程序的五十種方法

編輯:C#入門知識

一、用屬性代替可訪問的字段
1、.NET數據綁定只支持數據綁定,使用屬性可以獲得數據綁定的好處;
2、在屬性的get和set訪問器重可使用lock添加多線程的支持。   


二、readonly(運行時常量)和const(編譯時常量)
1、const只可用於基元類型、枚舉、字符串,而readonly則可以是任何的類型;
2、const在編譯時將替換成具體的常量,這樣如果在引用中同時使用了const和readonly兩種值,則對readonly的再次改變將會改變設計的初衷,這是需要重新編譯所更改的程序集,以重新引用新的常量值。
3、const比readonly效率高,但失去了應用的靈活性。   


三、is與as
1、兩者都是在運行時進行類型的轉換,as操作符只能使用在引用類型,而is可以使用值和引用類型;
2、通常的做法是用is判斷類型,然後選擇使用as或強類型轉換操作符(用operater定義的轉換)有選擇地進行。   


四、ConditionalAttribute代替#if #endif條件編譯
1、ConditionalAttribute只用於方法級,對其他的如類型、屬性等的添加都是無效的;而#if #endif則不受此限制;
2、ConditionalAttribute可以添加多個編譯條件的或(OR)操作,而#if #endif則可以添加與(AND)[這裡可以完全定義為另一個單獨的符號];
3、ConditioanlAttribute定義可以放在一個單獨的方法中,使得程序更為靈活。   


五、提供ToString()方法
1、可以更友好的方式提供用戶詳細的信息;
2、使用IFormatter.ToString()方法提供更靈活的定制,如果添加IFormatProvider 和ICustomFormatter接口則更有意義的定制消息輸出。   


六、值和引用類型的區別
1、值類型不支持多態,適合存儲應用程序操作的數據,而引用則支持多態,適用於定義應用程序的行為;
2、對於數組定義為值類型可以顯著提高程序的性能;
3、值類型具有較少的堆內存碎片、內存垃圾和間接訪問時間,其在方法中的返回是以復制的方式進行,避免暴露內部結構到外界;
4、值類型應用在如下的場景中:類型的職責主要是用於數據存儲;公共接口完全由一些數據成員存取屬性定義;永遠沒有子類;永遠沒有多態行為。   


七、值類型盡可能實現為常量性和原子性的類型
1、使我們的代碼更易於編寫和維護;
2、初始化常量的三種策略:在構造中;工廠方法;構造一個可變的輔助類(如StringBuilder)。   


八、確保0為值得有效狀態
1、值類型的默認狀態應為0;
2、枚舉類型的0不應為無效的狀態;在FlagsAttribute是應確保0值為有效地狀態;


3、在字符串為為空時可以返回一個string.Empty的空字符串。   


九、相等判斷的多種表示關系
1、ReferenceEquals()判斷引用相等,需要兩個是引用同一個對象時方可返回true;
2、靜態的Equals()方法先進行引用判斷,再進行值類型判斷的;
3、對於引用類型的判斷可以在使用值語義時使用重寫Equals()方法;
4、重寫Equals()方法時也應當重寫GetHashCode()方法,同時提供operater==()操作。   


十、理解GetHashCode()方法的缺陷
1、GetHashCode()僅應用在基於散列的**定義鍵的散列值,如HashTable或Dictionary;
2、GetHashCode()應當遵循相應的三條規則:兩個相等對象應當返回相同的散列碼;應當是一個實例不變式;散列函數應該在所有的整數中產生一個隨機的分布。   


十一、優先使用foreach循環語句
1、foreach可以消除編譯器對for循環對數組邊界的檢查;
2、foreach的循環變量是只讀的,且存在一個顯式的轉換,在**對象的對象類型不正確時拋出異常;
3、foreach使用的**需要有:具備公有的GetEnumberator()方法;顯式實現了IEnumberable接口;實現了IEnumerator接口;
4、foreach可以帶來資源管理的好處,因為如果編譯器可以確定IDisposable接口時,可以使用優化的try…finally塊;   


十二、默認字段的初始化優於賦值語句
1、字段生命默認會將值類型初始化為0,引用類型初始化為null;
2、對同一個對象進行多次初始化會降低代碼的執行效率;
3、將字段的初始化放到構造器中有利於進行異常處理。   


十三、使用靜態構造器初始化靜態成員
1、靜態構造器會在一個類的任何方法、變量或者屬性訪問之前執行;
2、靜態字段同樣會在靜態構造器之前運行,同時靜態構造器有利於異常處理。   


十四、利用構造器鏈(在.NET 4.0已經用可選參數解決了這個問題)
1、用this將初始化工作交給另一個構造器,用base調用基類的構造器;
2、類型實例的操作順序是:將所有的靜態字段都設置為0;執行靜態字段初始化器;執行基類的靜態構造器;執行當前類型的靜態構造器;
將所有的實例字段設置為0;執行實例字段初始化器;執行合適的基類實例構造器;執行當前類型的實例構造器。   


十五、利用using和try/finally語句來清理資源
在IDisposable接口的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器不再執行終結操作。   


十六、盡量減少內存垃圾
1、分配和銷毀一個堆上的對象都要花費額外的處理器時間;
2、減少分配對象數量的技巧:經常使用的局部變量提升為字段;提供一個類,用於存儲Singleton對象來表達特定類型的常用實例。
3、用StringBuilder進行復雜的字符串操作。   


十七、盡量減少裝箱和拆箱
1、關注一個類型到System.Object的隱式轉換,同時值類型不應該被替換為System.Object類型;
2、使用接口而不是使用類型可以避免裝箱,即將值類型從接口實現,然後通過接口調用成員。   


十八、實現標准Dispose模式
1、使用非內存資源,它必須有一個終結器,垃圾收集器在完成沒有終結其的內存對象後,會將實現了終結器對象的添加到終結隊列中,然後垃圾收集器會啟動一個新的線程來運行這些對象上的終結器,這種防御性的變成方式是因為如果用戶忘記了調用Dispose()方法,垃圾回收器總是會調用終結器方法的,這樣可以避免出現非托管的內存資源不被釋放引起內存洩漏的問題;
2、使用IDisposable.Dispose()方法需要做四個方面的工作:釋放所有的非托管資源;釋放所有的托管資源;設置一個狀態標記來表示是否已經執行了Dispose();調用GC.SuppressFinalize(this)取消對象的終結操作;
3、為需要多態的類型添加一個受保護的虛方法Dispose(),派生類通過重寫這個方法來釋放自己的任務;
4、在需要IDisoposable接口的類型中,即使我們不需要一個終結器也應該實現一個終結器。   


十九、定義並實現接口優於繼承類型
1、不相關的類型可以共同實現一個共同的接口,而且實現接口比繼承更容易;
2、接口比較穩定,他將一組功能封裝在一個接口中,作為其他類型的實現合同,而基類則可以隨著時間的推移進行擴展。   


二十、明辨接口實現和虛方法重寫
1、在基類中實現一個接口時,派生類需要使用new來隱藏對基類方法的使用;
2、可以將基類接口的方法申明為虛方法,然後再派生類中實現。   


二十一、使用委托表達回調
1、委托對象本身不提供任何異常捕獲,所以任何的多播委托調用都會結束整個調用鏈;
2、通過顯示調用委托鏈上的每個委托目標可以避免多播委托僅返回最後一個委托的輸出。   


二十二、使用事件定義外部接口
1、應當聲明為共有的事件,讓編譯器為我們創建add和renmove方法;
2、使用System.ComponentModel.EventHandlerList容器來存儲各個事件處理器,在類型中包含大量事件時可以使用他來隱藏所有事件的復雜性。   


二十三、避免返回內部類對象的引用
1、由於值類型對象的訪問會創建一個該對象的副本,所以定義一個值類型的的屬性完全不會改變類型對象內部的狀態;
2、常量類型可以避免改變對象的狀態;
3、定義接口將訪問限制在一個子集中從而最小化對對象內部狀態的破壞;
4、定義一個包裝器對象來限制另一個對象的訪問;
5、希望客戶代碼更改內部數據元素時可以實現Observer模式,以使對象可以對更改進行校驗或相應。   


二十四、聲明式編程優於命令式編程
可以避免在多個類似的手工編寫的算法中犯錯誤的可能性,並提供清晰和可讀的代碼。   


二十五、盡可能將類型實現為可序列化的類型
1、類型表示的不是UI控件、窗口或者表單,都應使類型支持序列化;
2、在添加了NonSerializedAttribute的反序列化的屬性時可以通過實現IDeserializationCallback的OnDeserialization()方法裝入默認值;
3、在版本控制中可以使用ISerializable接口來進行靈活的控制,同時提供一個序列化的構造器來根據流中的數據初始化對象,在實現時還要求SerializationFormatter異常的許可;
4、如果需要創建派生類則需要提供一個掛鉤方法供派生類使用。   


二十六、使用IComparable和IComparer接口實現排序關系
1、IComparable接口用於為類型實現最自然的排序關系,重載四個比較操作符,可以提供一個重載版的CompareTo()方法,讓其接受具體類型作為參數;
2、IComparer用於提供有別於IComparable的排序關系,或者為我們提供類型本身說沒有實現的排序關系。   


二十七、避免ICloneable接口
1、對於值類型永遠不需要支持ICloneable接口,使用默認的賦值操作即可;
2、對於可能需要支持ICloneable接口的基類,應該為其創造一個受保護的復制構造器,並應當避免支持IConeable接口。   


二十八、避免強制轉換操作符
通過使用構造器來代替轉換操作符可以使轉換工作變得更清晰,由於在轉換後使用的臨時對象,容易導致一些詭異的BUG。   


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


三十、盡可能實現CLS兼容的程序集
1、創建一個兼容的程序集需要遵循兩條規則:程序集中所有公有和受保護成員所使用的參數和返回值類型都必須與CLS兼容;任何與CLS不兼容的公有和受保護成員都必須有一個與CLS兼容的替代品;
2、可以通過顯式實現接口來避開CLS兼容類型檢查,及CLSCompliantAttribute不會檢查私有的成員的CLS兼容性。   


三十一、盡可能實現短小簡潔的方法
1、JIT編譯器以方法為單位進行編譯,沒有被調用的方法不會被JIT編譯;
2、如果將較長的Switch中的Case語句的代碼替換成一個一個的方法,則JIT編譯器所節省的時間將成倍增加;
3、短小精悍的方法並選擇較少的局部變量可以獲得優化的寄存器使用;
4、方法內的控制分支越少,JIT編譯器越容易將變量放入寄存器。   


三十二、盡可能實現小尺寸、高內聚的程序集
1、將所有的公有類以及共用的基類放到一些程序集中,把為公有類提供功能的工具類也放入同樣的程序集中,把相關的公有接口打包到他們自己的程序集中,最後處理遍布應用程序中水平位置的類;
2、原則上創建兩種組件:一種為小而聚合、具有某項特定功能的程序集,另一種為大而寬、包含共用功能的程序集。   


三十三、限制類型的可見性
1、使用接口來暴露類型的功能,可以使我們更方便地創建內部類,同時又不會限制他們在程序集外的可用性;
2、向外暴露的公有類型越少,未來擴展和更改實現所擁有的選擇就越多。   


三十四、創建大粒度的Web API
這是在機器之間的交易的頻率和載荷都降到最低,將大的操作和細粒度的執行放到服務器執行。   


三十五、重寫優於事件處理器
1、一個事件處理器拋出異常,則事件鏈上的其他處理器將不會被調用,而重寫的虛方法則不會出現這種情況;
2、重寫要比關聯事件處理器高效得多,事件處理器需要迭代整個請求列表,這樣占用了更多的CPU時間;
3、事件能在運行時響應,具有更多的靈活性,可以對同一個事件關聯多個響應;
4、通行的規則是處理一個派生類的事件是,重寫方式較好。   


三十六、合理使用.NET運行時診斷
1、System.Diagnostics.Debug\Trace\EventLog為運行時提供了程序添加診斷信息所需要的所有工具,EventLog提供入口時的應用程序能寫到系統事件日志中;
2、最後不要寫自己的診斷庫,.NET FCL 已經擁有了我們需要的核心庫。   


三十七、使用標准配置機制
1、.NET框架的System.Windows.Application類為我們定義了建立通用配置路徑的屬性;
2、Application.LocalAppDataPath 和 Application.userDataPath 會生成本地數據目錄和用戶數據的路徑名;
3、不要在ProgramFiles和Windows系統目錄中寫入數據,這些位置需要更高的安全權限,不要指望用戶擁有寫入的權限。   


三十八、定制和支持數據綁定
1、BindingMananger和CurrencyManager這兩個對象實現了控件和數據源之間的數據傳輸;
2、數據綁定的優勢:使用數據綁定要比編寫自己的代碼簡單得多;應該將它用於文本數據項之外的范圍 —— 其他顯示屬性也可以被綁定;對於 Windowos Forms 數據綁定能夠處理多個控件同步的檢查相關數據源;
3、在對象不支持所需的屬性時,可以通過屏蔽當前的對象,然後添加一個想要的對象來支持數據綁定。   


三十九、使用.NET驗證
1、ASP.NET中有五種控件來驗證有效性,可以用CustomValidator派生一個新類來增加自己的認證器;
2、Windows驗證需要子System.Windows.Forms.Control.Validating寫一個事件處理器。   


四十、根據需要選用恰當的**
1、數組有兩個比較明顯的缺陷:不能動態的調整大小;調整大小非常耗時;
2、ArrayList混合了一維數組和鏈表的特征,Queue和Stack是建立在Array基礎上的特殊數組;
3、當程序更加靈活的添加和刪除項時,可以使更加健壯的**類型,當創建一個模擬**的類時,應當為其實現索引器和IEnumberable接口。   

 


四十一、DataSet優於自定義結構
1、DataSet有兩個缺點個:使用XML序列化機制的DataSet與非.NET 代碼之間的交互不是很好;DataSet是一個非常通用的容器;
2、強類型的DataSet打破了更多的設計規則,其獲得的開發效率要遠遠高於自己編寫的看上去更為優雅的設計。   


四十二、利用特性簡化反射
通過設計和實現特性類,強制開發人員用他們來聲明可被動態使用的類型、方法和屬性,可以減少應用程序的運行時錯誤,提高軟件的用戶滿意度。   


四十三、避免過度使用反射
1、Invoke成員使用的參數和返回值都是System.Object,在運行時進行類型的轉換,但出現問題的可能性也變得更多了;
2、接口使我們可以得到一個更為清晰、也更具可維護性的系統,反射是一個很強大的晚期綁定機制,.NET框架使用它來實現Windows控件和Web控件的數據綁定。   


四十四、為應用程序創建特定的異常類
1、需要不同的異常類的唯一原因是讓用戶在編寫catch處理器時能夠方便地對不同的錯誤采取不同的做法;
2、可能有不同的修復行為時,我們才應該創建多種不同的異常類,通過提供異常基類所支持的所有構造器,可以為應用程序創建功能完整的異常類,使用InnerException屬性可以保存更低級別錯誤條件所產生的所有錯誤信息。   


四十五、優先選擇異常安全保證
1、強異常保證在從異常中恢復和簡化異常處理之間提供了最好的平衡,在操作因為異常而中斷,程序的狀態保留不變;
2、對將要修改的數據做防御性的復制,對這些數據的防御性復制進行修改,這中間的操作可能會引發異常,將臨時的副本和原對象進行交換;
3、終結器、Dispose()方法和委托對象所綁定的目標方法在任何情況下都應當確保他們不會拋出異常。   


四十六、最小化互操作
1、互操作有三個方面的代價:數據在托管堆和非托管堆之間的列舉成本,托管代碼和非托管代碼之間切換的成本,對開發人員來說與混合環境打交道的開發工作;
2、在interop中使用blittable類型可以有效地在托管和非托管環境中來回復制,而不受對象內部結構的影響;
3、使用In/Out特性來確保最貼切的不必要的多次復制,通過聲明數據如何被列舉來提高性能;
4、使用COM Interop用最簡單的方式實現和COM組件的互操作,使用P/Invoke調用Win32 API,或者使用C++編譯器的/CLR開關來混合托管和非托管的代碼;   


四十七、優先選擇安全代碼
1、盡可能的避免訪問非托管內存,隔離存儲不能防止來自托管代碼和受信用戶的訪問;
2、程序集在Web上運行時可以考慮使用隔離存儲,當某些算法確實需要更高的安全許可時,應該將那些代碼隔離在一個單獨的程序集中。   


四十八、掌握相關工具與資源
1、使用NUnit建立自動單元測試(集成在VS2010 中了);
2、FXCop工具會獲取程序集中的IL代碼,並將其與異族編碼規則和最佳實踐對照分析,最後報告違例情況;
3、ILDasm是一個IL反匯編工具,可以幫助我們洞察細節;
4、Shared Source CLI是一個包含.NET框架內核和C#編譯器的實現源碼。

四十九、為C#2.0做准備(這個規則現在已經沒什麼意義了,畢竟現在已經到了4.0 )
五十、了解ECMA標准

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved