Effective C# 要點小結,不懂也得寫

wzw_ice發表於2018-07-21

Effective C# 要點小結,不懂也得寫

Item 1: Use Properties Instead of Accessible Data Members
資料繫結支援屬性不支援public資料成員
屬性的方法實現中,對get和set訪問器使用鎖提供資料同步訪問

Item 2: Prefer readonly to const
readonly執行時常量;const編譯時常量,只能用於基元型別、列舉、字串
const在編譯時將替換成具體的常量,引用時若未編譯所有程式集,const常量值不會更新,readonly常量值更新。
const效能較高但靈活性相對低

Item 3: Prefer the is or as Operators to Casts
使用者自定義型別轉換隻針對編譯時期型別,執行時期對使用者自定義轉換不會執行
as進行型別轉換會返回null,不能用於值型別(值型別不可能為null)
不支援as轉換時,先使用is進行異常去除或轉換,再用as進行轉換
型別約束、強制型別轉換

Item 4: Use Conditional Attributes Instead of #if
條件特性只能運用於方法,條件特性方法返回值必是void,沒有引數
多個條件特性逗號連線表示OR操作,使用預編譯塊可設定條件特性AND操作
#if ( VAR1 && VAR2 )
#define BOTH
#endif

Item 5: Always Provide ToString()
友好資訊輸出
System.IFormattable.ToString()格式化字串輸出介面 一些格式必要實現
新增IFormatProvider 和ICustomFormatter介面定製訊息輸出

Item 6: Understand the Relationships Among the Many Different Concepts of Equality
ReferenceEquals()判斷引用相等
靜態Equals()先引用判斷再值判斷
引用型別的判斷時,使用值語義時使用重寫Equals()方法
建立值型別時重寫operator==()

Item 7: Understand the Pitfalls of GetHashCode()
GetHashCode()僅應用在基於雜湊的集合定義鍵的雜湊值,如HashTable< T >或Dictionary< K,V >
重寫GetHashCode()條件:相等物件返回相同雜湊值;物件的GetHashCode()必是例項不變數;針對所有輸入產生整數隨機分佈

Item 8: Prefer Query Syntax to Loops
查詢語法相比迴圈結構能建立多種組合的API
使用.AsParallel()能夠並行執行查詢

Item 9: Avoid Conversion Operators in Your APIs
隱式轉換操作 當一個型別轉換到另一個型別
顯式轉換 當程式碼中強制轉換
轉換會再編譯時丟失物件
當替代物件可用,即為臨時物件或能訪問內部域

Item 10: Use Optional Parameters to Minimize Method Overloads
使用預設引數和命令引數可以創造使用者想要的過載組合
建立新版本時必須建立額外引數的過載
避免更改引數名稱

Item 11: Understand the Attraction of Small Functions
小函式可讓JIT編譯器很容易支援暫存器化–處理器選擇暫存器而非棧儲存區域性變數
小函式更可能使用內聯方式呼叫

Item 12: Prefer Member Initializers to Assignment Statements
初始化語句在基類建構函式之前執行
變數宣告時初始化避免多個建構函式和成員變數難以保持同步
不應使用初始化語句的情況:值型別物件初始化為0或引用型別初始化為null;變數在所有建構函式中初始化是不同的;建構函式中初始化便於異常處理

Item 13: Use Proper Initialization for Static Class Members
靜態構造器會在一個類的任何方法、變數或者屬性訪問之前執行
靜態欄位同樣會在靜態構造器之前執行,同時靜態構造器有利於異常處理。

Item 14: Minimize Duplicate Initialization Logic
建構函式鏈即一個建構函式呼叫另一個建構函式
建構函式使用預設引數有利於減少建構函式的過載
物件變數初始化的過程:static變數預設存0、static變數初始化執行、基類靜態建構函式執行、靜態建構函式執行、例項成員變數預設存0、例項成員變數初始化、恰當的基類例項建構函式執行、例項建構函式執行

Item 15: Utilize using and try/finally for Resource Cleanup
非託管資源型別必須使用 IDisposable 介面的 Dispose() 方法釋放
using塊能確保可回收物件被恰當回收(保證了Dispose的執行),一個using塊相當於一個try/finally塊
並非所有物件實現了IDisposable,可使用as進行轉換
在IDisposable介面的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器不再執行解構函式

Item 16: Avoid Creating Unnecessary Objects
引用型別的區域性變數使用頻繁,應該將其提升為成員變數,或者,使用單例模式讓一個類提供常用的例項
StringBuilder可變字串類,進行復雜的字串操作

Item 17: Implement the Standard Dispose Pattern
非記憶體資源使用時必有終結器,GC完成沒有終結器的記憶體物件後,實現終結器物件的新增,啟動新的執行緒執行終結器
實現IDisposable.Dispose()主要任務:釋放所有的非託管資源;釋放所有的託管資源;設定一個狀態標記來表示是否已經執行了Dispose();呼叫GC.SuppressFinalize(this)取消物件的終結操作
為需要多型的型別新增一個受保護的虛方法Dispose(),派生類通過重寫這個方法來釋放自己的任務

Item 18: Distinguish Between Value Types and Reference Types
值型別不支援多型,適合儲存應用程式操作的資料,而引用則支援多型,適用於定義應用程式的行為
值型別具有較少的堆記憶體碎片、記憶體垃圾和間接訪問時間,其在方法中的返回是以複製的方式進行,避免暴露內部結構到外界
值型別應用場景:資料儲存、公共介面完全由一些資料成員存取屬性定義、沒有子類、沒有多型性為

Item 19: Ensure That 0 Is a Valid State for Value Types
值型別的預設為0
列舉型別的0不應為無效的狀態;在FlagsAttribute是應確保0值為有效的狀態
在字串為空時可以返回一個string.Empty的空字串

Item 20: Prefer Immutable Atomic Value Types
不可變型別修改狀態需要建立新的物件而非修改已存在的例項
變數初始化三種策略:構造器、工廠方法、構造可變輔助類(如StringBuilder)

Item 21: Limit Visibility of Your Types
public訪問限制儘可能少的使用
public區域越少有助於覆蓋率單元測試
public類越少,擴充套件和修改實現的選擇越多

Item 22: Prefer Defining and Implementing Interfaces to Inheritance
介面描述物件的表現方式,不提供成員變數,無實現;基類描述物件是什麼,提供子類一些實現描述共有行為
介面比較穩定,將功能封裝在一個介面中;基類可進行擴充套件

Item 23: Understand How Interface Methods Differ from Virtual Methods
介面宣告的成員是非虛的
虛擬函式適用於繼承關係的類物件
行內函數、建構函式、靜態成員函式不能為虛擬函式,解構函式可以為虛擬函式
在基類中實現一個介面時,派生類需要使用new來隱藏對基類方法的使用

Item 24: Express Callbacks with Delegates
委託物件本身不提供任何異常捕獲,所以任何的多播委託呼叫都會結束整個呼叫鏈
通過顯示呼叫委託鏈上的每個委託目標可以避免多播委託僅返回最後一個委託的輸出。
委託提供了執行時利用回撥的最好方式,可在執行時確定委託目標

Item 25: Implement the Event Pattern for Notifications
.Net事件模式就是觀察者模式的語法規範
事件是構建在委託之上提供型別安全函式簽名的處理

{
delegate: 就是原始的委託,可以理解為方法指標或方法的簽名
Action: 是沒有返回值的泛型委託
Func:有返回值的泛型委託
Predicate<>:返回值為 bool 型別的謂詞泛型委託
Effective C# 改善C#程式的50種方法
event:對 delegate 的封裝
}

Item 26: Avoid Returning References to Internal Class Objects
四種策略保證內部資料結構不被任意修改:值型別、不可變型別、介面、包裝器

Item 27: Prefer Making Your Types Serializable
.NET序列化會將物件的所有成員儲存到輸出流中
使用序列化特性[Serializable],不可序列化成員預設初始化值0或null,如果預設不正確,需實現IDeserialization介面進行初始化

Item 28: Create Large-Grain Internet Service APIs
伺服器和客戶端高效通訊的兩個權衡點:資料量和頻率–一次資料請求的完整性和網路寬頻負載

Item 29: Support Generic Covariance and Contravariance
型別可變性:協變(使用一般型別的地方可以傳入特殊型別的物件)和逆變(使用特殊型別的地方可以傳入一般型別的物件)
泛型和委託
in逆變 out協變 函式引數修飾介面

Item 30: Prefer Overrides to Event Handlers
系統事件處理方式:事件處理器、過載基類的虛擬函式
一個事件處理器丟擲異常,則事件鏈上的其他處理器將不會被呼叫,而過載的虛方法則不會出現這種情況;
過載要比關聯事件處理器高效得多,事件處理器需要迭代整個請求列表,這樣佔用了更多的CPU時間;
事件在執行時響應,能夠掛載多個事件處理器
當基類有一個函式處理一個事件時,過載方式優先

Item 31: Implement Ordering Relations with IComparable and IComparer
IComparable介面用於為型別實現最自然的排序關係,過載四個比較操作符,可以提供一個過載版的CompareTo()方法,讓其接受具體型別作為引數;
IComparer用於提供有別於IComparable的排序關係,或者為我們提供型別本身說沒有實現的排序關係。

Item 32: Avoid ICloneable
對於可能需要支援ICloneable介面的基類,應該為其創造一個受保護的複製構造器,並應當避免支援IConeable介面
不管是值型別還是引用型別如果實現了 ICloneable 介面,這個類的成員變數和繼承結構也要實現 ICloneable ,才能做到深複製和淺複製的一致性。

Item 33: Use the new Modifier Only to React to Base Class Updates
儘量不用new修飾符,僅當基類更新時使用

Item 34: Avoid Overloading Methods Defined in Base Classes
重寫基類的方法時,只有強制型別轉換時才會呼叫基類的方法
重寫解析規則是優先選擇編譯時繼承結構最底端的子類的方法

Item 35: Learn How PLINQ Implements Parallel Algorithms
分割槽演算法:範圍分割槽、塊分割槽、帶分割槽、雜湊分割槽
另三個並行任務的演算法:Pipelining、Stop&Go、Inverted Enumeration

Item 36: Understand How to Use PLINQ for I/O Bound Operations
PLINQ 使用固定的執行緒數量
Parallel Task 庫和 PLINQ 比之前的庫提供更好的語言層次對非同步程式設計的支援

Item 37: Construct Parallel Algorithms with Exceptions in Mind
如果出現異常你的並行演算法就必須支援回滾
Parallel Task 庫使用
AggregateExceptoion 類持有並行演算法排除的所有異常,必須處理AggregateException 以控制執行緒初始化所有後臺工作

Item 38: Understand the Pros and Cons of Dynamic
表示式更適合簡單的計算
構建基於已存在型別的特定成員的方法的最好的方式是編寫動態方法並且在執行時推斷具體的選擇。動態實現要找到一個恰當的實現,使用並快取有助於更好的效能。它比單純的靜態型別解決方法更耗時,卻比表示式樹的解析更簡單。

Item 39: Use Dynamic to Leverage the Runtime Type of Generic Type Parameters
//編寫一個泛型方法並利用執行時形象進行型別轉換
//Convert 與 Cast 的比較中,ConverT 適用於更多的型別轉換的情況
//泛型方法不知道型別引數代表的型別的特定的功能,動態可使執行時反射使問題得到解決
使用泛型引數型別對動態物件進行強制型別轉換。

Item 40: Use Dynamic for Parameters That Receive Anonymous Types
匿名引數的一個缺陷是你不能輕易讓一個方法使用其作為引數或返回值
動態呼叫意味著需要支付額外的開銷
當需要一到兩個使用匿名型別的實用方法,動態呼叫是建立這個行為的簡單的方法

Item 41: Use DynamicObject or IDynamicMetaObjectProvider for Data-Driven Dynamic Types
System.Dynamic.DynamicObject或System.Dynamic.IDynamicMetaObjectProvider建立自定義型別實現動態功能
動態型別的建立首要採用System.Dynamic.DynamicObject,當必須使用不同的基類,實現IDynamicMetaObjectProvider

Item 42: Understand How to Make Use of the Expression API
使用表示式和表示式樹 比使用反射類更優
lambda可以被編譯成委託
表示式API應用的兩種方法:建立引數為表示式的方法;在執行時建立程式碼

Item 43: Use Expressions to Transform Late Binding into Early Binding
實現INotifyPropertyChanged和INotifyPropertyChanging消除對屬性名的依賴

Item 44: Minimize Dynamic Objects in Public APIs
動態引數的方法返回值也是動態的,需要限制動態執行範圍,返回靜態型別
當程式碼依賴於在另一個環境中建立的動態型別,將其封裝動態物件,並使用不同的靜態型別提供公共介面

Item 45: Minimize Boxing and Unboxing
裝箱把值型別轉換成引用型別
使用泛型類和泛型方法能避免裝拆箱操作
注意一個型別到System.Object的隱式轉換,同時值型別不應該被替換為System.Object型別

Item 46: Create Complete Application-Specific Exception Classes
可能有不同的修復行為時,我們才應該建立多種不同的異常類,通過提供異常基類所支援的所有構造器,可以為應用程式建立功能完整的異常類,使用InnerException屬性可以儲存更低階別錯誤條件所產生的所有錯誤資訊

Item 47: Prefer the Strong Exception Guarantee
強異常保證在從異常中恢復和簡化異常處理之間提供了最好的平衡,在操作因為異常而中斷,程式的狀態保留不變
終結器、Dispose()方法和委託物件所繫結的目標方法在任何情況下都應當確保他們不會丟擲異常

Item 48: Prefer Safe Code
常見受保護的資源為非託管資源和檔案系統,其他受保護的資源包括資料庫、網路埠、Windows登錄檔和列印子系統
儘可能的避免訪問非託管記憶體,隔離儲存不能防止來自託管程式碼和受信使用者的訪問;
程式集在Web上執行時可以考慮使用隔離儲存,當某些演算法確實需要更高的安全許可時,應該將那些程式碼隔離在一個單獨的程式集中

Item 49: Prefer CLS-Compliant Assemblies
要建立相容cls的總成,您必須遵循兩條規則。首先,來自公共和受保護成員的所有引數和返回值的型別必須CLS相容的。第二,任何不遵守規則的公眾或受保護的成員必須有一個符合cls的同義詞。

Item 50: Prefer Smaller, Cohesive Assemblies
為特定功能建立小的聚集的元件,為常用功能建立大的寬泛的元件

相關文章