【《Effective C#》提炼总结】提高Unity中C#代码质量的21条准则
原則1 ??盡可能地使用屬性而不是可直接訪問(wèn)的數(shù)據(jù)成員
? ? ? ?
●?屬性(property)一直是C#語(yǔ)言中比較有特點(diǎn)的存在。屬性允許將數(shù)據(jù)成員作為共有接口的一部分暴露出去,同時(shí)仍舊提供面向?qū)ο蟓h(huán)境下所需的封裝。屬性這個(gè)語(yǔ)言元素可以讓你像訪問(wèn)數(shù)據(jù)成員一樣使用,但其底層依舊是使用方法實(shí)現(xiàn)的。
?
●?使用屬性,可以非常輕松的在get和set代碼段中加入檢查機(jī)制。
?
需要注意,正因?yàn)閷傩允怯梅椒▽?shí)現(xiàn)的,所以它擁有方法所擁有的一切語(yǔ)言特性:
1)屬性增加多線程的支持是非常方便的。你可以加強(qiáng) get 和 set 訪問(wèn)器(accessors)的實(shí)現(xiàn)來(lái)提供數(shù)據(jù)訪問(wèn)的同步。
2)屬性可以被定義為virtual。
3)可以把屬性擴(kuò)展為abstract。
4)可以使用泛型版本的屬性類型。
5)屬性也可以定義為接口。
6)因?yàn)閷?shí)現(xiàn)實(shí)現(xiàn)訪問(wèn)的方法get與set是獨(dú)立的兩個(gè)方法,在C# 2.0之后,你可以給它們定義不同的訪問(wèn)權(quán)限,來(lái)更好的控制類成員的可見(jiàn)性。
7)而為了和多維數(shù)組保持一致,我們可以創(chuàng)建多維索引器,在不同的維度上使用相同或不同類型。
?
無(wú)論何時(shí),需要在類型的公有或保護(hù)接口中暴露數(shù)據(jù),都應(yīng)該使用屬性。如果可以也應(yīng)該使用索引器來(lái)暴露序列或字典。現(xiàn)在多投入一點(diǎn)時(shí)間使用屬性,換來(lái)的是今后維護(hù)時(shí)的更加游刃有余。
?
?
原則2 ??偏向于使用運(yùn)行時(shí)常量而不是編譯時(shí)常量
? ? ? ?
對(duì)于常量,C#里有兩個(gè)不同的版本:運(yùn)行時(shí)常量(readonly)和編譯時(shí)常量(const)。
?
應(yīng)該盡量使用運(yùn)行時(shí)常量,而不是編譯器常量。雖然編譯器常量略快,但并沒(méi)有運(yùn)行時(shí)常量那么靈活。應(yīng)僅僅在那些性能異常敏感,且常量的值在各個(gè)版本之間絕對(duì)不會(huì)變化時(shí),再使用編譯時(shí)常量。
?
編譯時(shí)常量與運(yùn)行時(shí)常量不同之處表現(xiàn)在于他們的訪問(wèn)方式不同,因?yàn)镽eadonly值是運(yùn)行時(shí)解析的:
●?編譯時(shí)常量(const)的值會(huì)被目標(biāo)代碼中的值直接取代。
●?運(yùn)行時(shí)常量(readonly)的值是在運(yùn)行時(shí)進(jìn)行求值。●?引用運(yùn)行時(shí)生成的IL將引用到readonly變量,而不是變量的值。
?
這個(gè)差別就帶來(lái)了如下規(guī)則:
●?編譯時(shí)常量(const)僅能用于數(shù)值和字符串。
●?運(yùn)行時(shí)常量(readonly)可以為任意類型。運(yùn)行時(shí)常量必須在構(gòu)造函數(shù)或初始化器中初始化,因?yàn)樵跇?gòu)造函數(shù)執(zhí)行后不能再被修改。你可以讓某個(gè)readonly值為一個(gè)DataTime結(jié)構(gòu),而不能指定某個(gè)const為DataTIme。
●?可以用readonly值保存實(shí)例常量,為類的每個(gè)實(shí)例存放不同的值。而編譯時(shí)常量就是靜態(tài)的常量。
●?有時(shí)候你需要讓某個(gè)值在編譯時(shí)才確定,就最好是使用運(yùn)行時(shí)常量(readonly)。
●?標(biāo)記版本號(hào)的值就應(yīng)該使用運(yùn)行時(shí)常量,因?yàn)樗闹禃?huì)隨著每個(gè)不同版本的發(fā)布而改變。
●?const優(yōu)于readonly的地方僅僅是性能,使用已知的常量值要比訪問(wèn)readonly值略高一點(diǎn),不過(guò)這其中的效率提升,可以說(shuō)是微乎其微的。
?
綜上,在編譯器必須得到確定數(shù)值時(shí),一定要使用const。例如特性(attribute)的參數(shù)和枚舉的定義,還有那些在各個(gè)版本發(fā)布之間不會(huì)變化的值。除此之外的所有情況,都應(yīng)盡量選擇更加靈活的readonly常量。
?
?
原則3 ??推薦使用is 或as操作符而不是強(qiáng)制類型轉(zhuǎn)換
? ? ? ? ? ??
●?C#中,is和as操作符的用法概括如下:
is :?檢查一個(gè)對(duì)象是否兼容于其他指定的類型,并返回一個(gè)Bool值,永遠(yuǎn)不會(huì)拋出異常。
as:作用與強(qiáng)制類型轉(zhuǎn)換是一樣,但是永遠(yuǎn)不會(huì)拋出異常,即如果轉(zhuǎn)換不成功,會(huì)返回null。
?
●?盡可能的使用as操作符,因?yàn)橄鄬?duì)于強(qiáng)制類型轉(zhuǎn)換來(lái)說(shuō),as更加安全,也更加高效。
?
●?as在轉(zhuǎn)換失敗時(shí)會(huì)返回null,在轉(zhuǎn)換對(duì)象是null時(shí)也會(huì)返回null,所以使用as進(jìn)行轉(zhuǎn)換時(shí),只需檢查返回的引用是否為null即可。
?
●?as和is操作符都不會(huì)執(zhí)行任何用戶自定義的轉(zhuǎn)換,它們僅當(dāng)運(yùn)行時(shí)類型符合目標(biāo)類型時(shí)才能轉(zhuǎn)換成功,也不會(huì)在轉(zhuǎn)換時(shí)創(chuàng)建新的對(duì)象。
?
●?as運(yùn)算符對(duì)值類型是無(wú)效,此時(shí)可以使用is,配合強(qiáng)制類型轉(zhuǎn)換進(jìn)行轉(zhuǎn)換。
?
●?僅當(dāng)不能使用as進(jìn)行轉(zhuǎn)換時(shí),才應(yīng)該使用is操作符。否則is就是多余的。
?
原則4 ??推薦使用條件屬性而不是#if條件編譯
? ? ? ? ? ??
●?由于#if/#endif很容易被濫用,使得編寫(xiě)的代碼難于理解且更難于調(diào)試。C#為此提供了一條件特性(Conditional attribute)。使用條件特性可以將函數(shù)拆分出來(lái),讓其只有在定義了某些環(huán)境變量或設(shè)置了某個(gè)值之后才能編譯并成為類的一部分。Conditional特性最常用的地方就是將一段代碼變成調(diào)試語(yǔ)句。
?
●?Conditional特性只可應(yīng)用在整個(gè)方法上,另外,任何一個(gè)使用Conditional特性的方法都只能返回void類型。不能再方法內(nèi)的代碼塊上應(yīng)用Conditional特性。也不可以在有返回值的方法上應(yīng)用Conditional特性。但應(yīng)用了Conditional特性的方法可以接受任意數(shù)目的引用類型參數(shù)。
?
●?使用Conditional特性生成的IL要比使用#if/#Eendif時(shí)更有效率。同時(shí),將其限制在函數(shù)層面上可以更加清晰地將條件性的代碼分離出來(lái),以便進(jìn)一步保證代碼的良好結(jié)構(gòu)。
?
?
原則5 ??理解幾個(gè)等同性判斷之間的關(guān)系
●?C#中可以創(chuàng)建兩種類型:值類型和引用類型。如果兩個(gè)引用類型的變量指向的是同一個(gè)對(duì)象,它們將被認(rèn)為是“引用相等”。如果兩個(gè)值類型的變量類型相同,而且包含同樣的內(nèi)容,它們被認(rèn)為是“值相等”。這也是等同性判斷需要如此多方法的原因。
?
●?當(dāng)我們創(chuàng)建自己的類型時(shí)(無(wú)論是類還是struct),應(yīng)為類型定義“等同性”的含義。C#提供了4種不同的函數(shù)來(lái)判斷兩個(gè)對(duì)象是否“相等”。
1)public static bool ReferenceEquals (object left, object right);判斷兩個(gè)不同變量的對(duì)象標(biāo)識(shí)(object identity)是否相等。無(wú)論比較的是引用類型還是值類型,該方法判斷的依據(jù)都是對(duì)象標(biāo)識(shí),而不是對(duì)象內(nèi)容。
2)public static bool Equals (object left, object right); 用于判斷兩個(gè)變量的運(yùn)行時(shí)類型是否相等。
3)public virtual bool Equals(object right); 用于重載
4)public static bool operator ==(MyClass left, MyClass right); 用于重載
?
●?不應(yīng)該覆寫(xiě)Object.referenceEquals()靜態(tài)方法和Object.Equals()靜態(tài)方法,因?yàn)樗鼈円呀?jīng)完美的完成了所需要完成的工作,提供了正確的判斷,并且該判斷與運(yùn)行時(shí)的具體類型無(wú)關(guān)。對(duì)于值類型,我們應(yīng)該總是覆寫(xiě)Object.Equals()實(shí)例方法和operatior==( ),以便為其提供效率更高的等同性判斷。對(duì)于引用類型,僅當(dāng)你認(rèn)為相等的含義并非是對(duì)象標(biāo)識(shí)相等時(shí),才需要覆寫(xiě)Object.Equals( )實(shí)例方法。在覆寫(xiě)Equals( )時(shí)也要實(shí)現(xiàn)IEquatable<T>。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則6。
?
?
原則6 ??了解GetHashCode( )的一些坑
●?GetHashCode( )方法在使用時(shí)會(huì)有不少坑,要謹(jǐn)慎使用。GetHashCode()函數(shù)僅會(huì)在一個(gè)地方用到,即為基于散列(hash)的集合定義鍵的散列值時(shí),此類集合包括HashSet<T>和Dictionary<K,V>容器等。對(duì)引用類型來(lái)講,索然可以正常工作,但是效率很低。對(duì)值類型來(lái)講,基類中的實(shí)現(xiàn)有時(shí)甚至不正確。而且,編寫(xiě)的自己GetHashCode( )也不可能既有效率又正確。
?
●?在.NET中,每個(gè)對(duì)象都有一個(gè)散列碼,其值由System.Object.GetHashCode()決定。
?
●?實(shí)現(xiàn)自己的GetHashCode( )時(shí),要遵循上述三條原則:
1)如果兩個(gè)對(duì)象相等(由operation==定義),那么他們必須生成相同的散列碼。否則,這樣的散列碼將無(wú)法用來(lái)查找容器中的對(duì)象。
2)對(duì)于任何一個(gè)對(duì)象A,A.GetHashCode()必須保持不變。
3)對(duì)于所有的輸入,散列函數(shù)應(yīng)該在所有整數(shù)中按隨機(jī)分別生成散列碼。這樣散列容器才能得到足夠的效率提升。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則7。
?
?
原則7 ??理解短小方法的優(yōu)勢(shì)
將C#代碼翻譯成可執(zhí)行的機(jī)器碼需要兩個(gè)步驟。
C#編譯器將生成IL,并放在程序集中。隨后,JIT將根據(jù)需要逐一為方法(或是一組方法,如果涉及內(nèi)聯(lián))生成機(jī)器碼。短小的方法讓JIT編譯器能夠更好地平攤編譯的代價(jià)。短小的方法也更適合內(nèi)聯(lián)。
?
除了短小之外,簡(jiǎn)化控制流程也很重要。控制分支越少,JIT編譯器也會(huì)越容易地找到最適合放在寄存器中的變量。
?
所以,短小方法的優(yōu)勢(shì),并不僅體現(xiàn)在代碼的可讀性上,還關(guān)系到程序運(yùn)行時(shí)的效率。——MyPS:我認(rèn)為這一條對(duì)于Python等其他語(yǔ)言也適用
?
PS:此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則11。
?
?
原則8 ?選擇變量初始化而不是賦值語(yǔ)句
成員初始化器是保證類型中成員均被初始化的最簡(jiǎn)單的方法——無(wú)論調(diào)用的是哪一個(gè)構(gòu)造函數(shù)。初始化器將在所有構(gòu)造函數(shù)執(zhí)行之前執(zhí)行。使用這種語(yǔ)法也就保證了你不會(huì)再添加的新的構(gòu)造函數(shù)時(shí)遺漏掉重要的初始化代碼。
?
綜上,若是所有的構(gòu)造函數(shù)都要將某個(gè)成員變量初始化成同一個(gè)值,那么應(yīng)該使用初始化器。(MyPS:在構(gòu)造器中處理,本質(zhì)上是對(duì)成員變量(即字段)進(jìn)行賦值——因?yàn)槭褂玫氖琴x值語(yǔ)句;我覺(jué)得,如果和構(gòu)造器參數(shù)無(wú)關(guān),那么直接在定義時(shí)初始化就好)
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則12。
?
?
原則9 ??正確地初始化靜態(tài)成員變量
●?C#提供了有靜態(tài)初始化器和靜態(tài)構(gòu)造函數(shù)來(lái)專門用于靜態(tài)成員變量的初始化。
?
●?靜態(tài)構(gòu)造函數(shù)是一個(gè)特殊的函數(shù),將在其他所有方法執(zhí)行之前以及變量或?qū)傩员坏谝淮卧L問(wèn)之前執(zhí)行。可以用這個(gè)函數(shù)來(lái)初始化靜態(tài)變量,實(shí)現(xiàn)單例模式或執(zhí)行類可用之前必須進(jìn)行的任何操作。
?
●?和實(shí)例初始化一樣,也可以使用初始化器語(yǔ)法來(lái)替代靜態(tài)的構(gòu)造函數(shù)。若只是需要為某個(gè)靜態(tài)成員分配空間,那么不妨使用初始化器的語(yǔ)法。而若是要更復(fù)雜一些的邏輯來(lái)初始化靜態(tài)成員變量,那么可以使用靜態(tài)構(gòu)造函數(shù)。
?
●?使用靜態(tài)構(gòu)造函數(shù)而不是靜態(tài)初始化器最常見(jiàn)的理由就是處理異常。在使用靜態(tài)初始化器時(shí),我們無(wú)法自己捕獲異常。而在靜態(tài)構(gòu)造函數(shù)中卻可以做到。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則13。
?
?
原則10 ??使用構(gòu)造函數(shù)鏈(減少重復(fù)的初始化邏輯)
? ? ??
●?編寫(xiě)構(gòu)造函數(shù)很多時(shí)候是個(gè)重復(fù)性的勞動(dòng),如果你發(fā)現(xiàn)多個(gè)構(gòu)造函數(shù)包含相同的邏輯,可以將這個(gè)邏輯提取到一個(gè)通用的構(gòu)造函數(shù)中。這樣既可以避免代碼重復(fù),也可以利用構(gòu)造函數(shù)初始化器來(lái)生成更高效的目標(biāo)代碼。
?
●?C#編譯器將把構(gòu)造函數(shù)初始化器看做是一種特殊的語(yǔ)法,并移除掉重復(fù)的變量初始化器以及重復(fù)的基類構(gòu)造函數(shù)調(diào)用。這樣使得最終的對(duì)象可以執(zhí)行最少的代碼來(lái)保證初始化的正確性。
?
●?構(gòu)造函數(shù)初始化器允許一個(gè)構(gòu)造函數(shù)去調(diào)用另一個(gè)構(gòu)造函數(shù)。而C# 4.0添加了對(duì)默認(rèn)參數(shù)的支持,這個(gè)功能也可以用來(lái)減少構(gòu)造函數(shù)中的重復(fù)代碼。你可以將某個(gè)類的所有構(gòu)造函數(shù)統(tǒng)一成一個(gè),并為所有的可選參數(shù)指定默認(rèn)值。其他的幾個(gè)構(gòu)造函數(shù)調(diào)用某個(gè)構(gòu)造函數(shù),并提供不同的參數(shù)即可。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則14。
?
?
原則11 ??實(shí)現(xiàn)標(biāo)準(zhǔn)的銷毀模式
●?GC可以高效地管理應(yīng)用程序使用的內(nèi)存。不過(guò)創(chuàng)建和銷毀堆上的對(duì)象仍舊需要時(shí)間。若是在某個(gè)方法中創(chuàng)建了太多的引用對(duì)象,將會(huì)對(duì)程序的性能產(chǎn)生嚴(yán)重的影響。
?
這里有一些規(guī)則,可以幫你盡量降低GC的工作量:
1)若某個(gè)引用類型(值類型無(wú)所謂)的局部變量用于被頻繁調(diào)用的例程中,那么應(yīng)該將其提升為成員變量。
2)為常用的類型實(shí)例提供靜態(tài)對(duì)象。
3)創(chuàng)建不可變類型的最終值。比如string類的+=操作符會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象并返回,多次使用會(huì)產(chǎn)生大量垃圾,不推薦使用。對(duì)于簡(jiǎn)單的字符串操作,推薦使用string.Format。對(duì)于復(fù)雜的字符串操作,推薦使用StringBuilder類。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則16。
?
?
原則12 ?區(qū)分值類型和引用類型
●?C#中,class對(duì)應(yīng)引用類型,struct對(duì)應(yīng)值類型。
?
●?C#不是C++,不能將所有類型定義成值類型并在需要時(shí)對(duì)其創(chuàng)建引用。C#也不是Java,不像Java中那樣所有的東西都是引用類型。你必須在創(chuàng)建時(shí)就決定類型的表現(xiàn)行為,這相當(dāng)重要,因?yàn)樯院蟮母目赡軒?lái)很多災(zāi)難性的問(wèn)題。
?
●?值類型無(wú)法實(shí)現(xiàn)多態(tài),因此其最佳用途就是存放數(shù)據(jù)。引用類型支持多態(tài),因此用來(lái)定義應(yīng)用程序的行為。
?
●??一般情況下,我們習(xí)慣用class,隨意創(chuàng)建的大都是引用類型,若下面幾點(diǎn)都肯定,那么應(yīng)該創(chuàng)建struct值類型:
1)該類型主要職責(zé)在于數(shù)據(jù)存儲(chǔ)嗎?
2)該類型的公有接口都是由訪問(wèn)其數(shù)據(jù)成員的屬性定義的嗎?
3)你確定該類型絕不會(huì)有派生類型嗎?
4)你確定該類型永遠(yuǎn)都不需要多態(tài)支持嗎?
?
●?用值類型表示底層存儲(chǔ)數(shù)據(jù)的類型,用引用類型來(lái)封裝程序的行為。這樣,你可以保證類暴露出的數(shù)據(jù)能以復(fù)制的形式安全提供,也能得到基于棧存儲(chǔ)和使用內(nèi)聯(lián)方式存儲(chǔ)帶來(lái)的內(nèi)存性能提升,更可以使用標(biāo)準(zhǔn)的面向?qū)ο蠹夹g(shù)來(lái)表達(dá)應(yīng)用程序的邏輯。而倘若你對(duì)類型未來(lái)的用圖不確定,那么應(yīng)該選擇引用類型。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則18。
?
原則13 ?保證0為值類型的有效狀態(tài)
在創(chuàng)建自定義枚舉值時(shí),請(qǐng)確保0是一個(gè)有效的選項(xiàng)。若你定義的是標(biāo)志(flag),那么可以將0定義為沒(méi)有選中任何狀態(tài)的標(biāo)志(比如None)。即作為標(biāo)記使用的枚舉值(即添加了Flags特性)應(yīng)該總是將None設(shè)置為0。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則19。
?
?
原則14
保證值類型的常量性和原子性
常量性的類型使得我們的代碼更加易于維護(hù)。不要盲目地為類型中的每一個(gè)屬性都創(chuàng)建get和set訪問(wèn)器。對(duì)于那些目的是存儲(chǔ)數(shù)據(jù)的類型,應(yīng)該盡可能地保證其常量性和原子性。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則20。
?
原則15 ??限制類型的可見(jiàn)性
在保證類型可以完成其工作的前提下。你應(yīng)該盡可能地給類型分配最小的可見(jiàn)性。也就是,僅僅暴露那些需要暴露的。盡量使用較低可見(jiàn)性的類來(lái)實(shí)現(xiàn)公有接口。可見(jiàn)性越低,能訪問(wèn)你功能的代碼越少,以后可能出現(xiàn)的修改也就越少。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則21。
?
?
原則16 ?通過(guò)定義并實(shí)現(xiàn)接口替代繼承
●?理解抽象基類(abstract class)和接口(interface)的區(qū)別:
1)接口是一種契約式的設(shè)計(jì)方式,一個(gè)實(shí)現(xiàn)某個(gè)接口的類型,必須實(shí)現(xiàn)接口中約定的方法。抽象基類則為一組相關(guān)的類型提供了一個(gè)共同的抽象。也就是說(shuō)抽象基類描述了對(duì)象是什么,而接口描述了對(duì)象將如何表現(xiàn)其行為。
?
2)接口不能包含實(shí)現(xiàn),也不能包含任何具體的數(shù)據(jù)成員。而抽象基類可以為派生類提供一些具體的實(shí)現(xiàn)。
?
3)基類描述并實(shí)現(xiàn)了一組相關(guān)類型間共用的行為。接口則定義了一組具有原子性的功能,供其他不相關(guān)的具體類型來(lái)實(shí)現(xiàn)。
?
●?理解好兩者之間的差別,我們便可以創(chuàng)造更富表現(xiàn)力、更能應(yīng)對(duì)變化的設(shè)計(jì)。使用類層次來(lái)定義相關(guān)的類型。用接口暴露功能,并讓不同的類型實(shí)現(xiàn)這些接口。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則22。
?
?
原則17 ?理解接口方法和虛方法的區(qū)別
第一眼看來(lái),實(shí)現(xiàn)接口和覆寫(xiě)虛方法似乎沒(méi)有什么區(qū)別,實(shí)際上,實(shí)現(xiàn)接口和覆寫(xiě)虛方法之間的差別很大。
1)接口中聲明的成員方法默認(rèn)情況下并非虛方法,所以,派生類不能覆寫(xiě)基類中實(shí)現(xiàn)的非虛接口成員。若要覆寫(xiě)的話,將接口方法聲明為virtual即可。
?
2)基類可以為接口中的方法提供默認(rèn)的實(shí)現(xiàn),隨后,派生類也可以聲明其實(shí)現(xiàn)了該接口,并從基類中繼承該實(shí)現(xiàn)。
?
3)實(shí)現(xiàn)接口擁有的選擇要比創(chuàng)建和覆寫(xiě)虛方法多。我們可以為類層次創(chuàng)建密封(sealed)的實(shí)現(xiàn),虛實(shí)現(xiàn)或者抽象的契約。還可以創(chuàng)建密封的實(shí)現(xiàn),并在實(shí)現(xiàn)接口的方法中提供虛方法進(jìn)行調(diào)用。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則23。
?
?
原則18 ??用委托實(shí)現(xiàn)回調(diào)
在C#中,回調(diào)是用委托來(lái)實(shí)現(xiàn)的,主要要點(diǎn)如下:
1)委托為我們提供了類型安全的回調(diào)定義。雖然大多數(shù)常見(jiàn)的委托應(yīng)用都和事件有關(guān),但這并不是C#委托應(yīng)用的全部場(chǎng)合。當(dāng)類之間有通信的需要,并且我們期望一種比接口所提供的更為松散的耦合機(jī)制時(shí),委托便是最佳的選擇。
?
2)委托允許我們?cè)谶\(yùn)行時(shí)配置目標(biāo)并通知多個(gè)客戶對(duì)象。委托對(duì)象中包含一個(gè)方法的應(yīng)用,該方法可以是靜態(tài)方法,也可以是實(shí)例方法。也就是說(shuō),使用委托,我們可以和一個(gè)或多個(gè)在運(yùn)行時(shí)聯(lián)系起來(lái)的客戶對(duì)象進(jìn)行通信。
?
3)由于回調(diào)和委托在C#中非常常用,以至于C#特地以lambda表達(dá)式的形式為其提供了精簡(jiǎn)語(yǔ)法。
?
4)由于一些歷史原因,.NET中的委托都是多播委托(multicast delegate)。多播委托調(diào)用過(guò)程中,每個(gè)目標(biāo)會(huì)被依次調(diào)用。委托對(duì)象本身不會(huì)捕捉任何異常。因此,任何目標(biāo)拋出的異常都會(huì)結(jié)束委托鏈的調(diào)用。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則24。
?
?
原則19 ?用事件模式實(shí)現(xiàn)通知
●?事件提供了一種標(biāo)準(zhǔn)的機(jī)制來(lái)通知監(jiān)聽(tīng)者,而C#中的事件其實(shí)就是觀察者模式的一個(gè)語(yǔ)法上的快捷實(shí)現(xiàn)。
?
●?事件是一種內(nèi)建的委托,用來(lái)為事件處理函數(shù)提供類型安全的方法簽名。任意數(shù)量的客戶對(duì)象都可以將自己的處理函數(shù)注冊(cè)到事件上,然后處理這些事件,這些客戶對(duì)象無(wú)需在編譯器就給出,事件也不必非要有訂閱者才能正常工作。
?
●?在C#中使用事件可以降低發(fā)送者和可能的通知接受者之間的耦合,發(fā)送者可以完全獨(dú)立于接受者進(jìn)行開(kāi)發(fā)。
?
PS: 此原則對(duì)應(yīng)于《EffectiveC# Second Edition》中原則25。
?
?
原則20 ??避免返回對(duì)內(nèi)部類對(duì)象的引用
●?若將引用類型通過(guò)公有接口暴露給外界,那么對(duì)象的使用者即可繞過(guò)我們定義的方法和屬性來(lái)更改對(duì)象的內(nèi)部結(jié)構(gòu),這會(huì)導(dǎo)致常見(jiàn)的錯(cuò)誤。
?
●?共有四種不同的策略可以防止類型內(nèi)部的數(shù)據(jù)結(jié)構(gòu)遭到有意或無(wú)意的修改:
1)值類型。當(dāng)客戶代碼通過(guò)屬性來(lái)訪問(wèn)值類型成員時(shí),實(shí)際返回的是值類型的對(duì)象副本。
2)常量類型。如System.String。
3)定義接口。將客戶對(duì)內(nèi)部數(shù)據(jù)成員的訪問(wèn)限制在一部分功能中。
4)包裝器(wrapper)。提供一個(gè)包裝器,僅暴露該包裝器,從而限制對(duì)其中對(duì)象的訪問(wèn)。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則26。
?
?
原則21 ??僅用new修飾符處理基類更新
●?使用new操作符修飾類成員可以重新定義繼承自基類的非虛成員。
?
●?new修飾符只是用來(lái)解決升級(jí)基類所造成的基類方法和派生類方法沖突的問(wèn)題。
?
●?new操作符必須小心使用。若隨心所欲的濫用,會(huì)造成對(duì)象調(diào)用方法的二義性。
?
PS: 此原則對(duì)應(yīng)于《Effective C# Second Edition》中原則33
轉(zhuǎn)載于:https://www.cnblogs.com/changbaishan/p/8716311.html
總結(jié)
以上是生活随笔為你收集整理的【《Effective C#》提炼总结】提高Unity中C#代码质量的21条准则的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解决每次从cmd进入sqlplus,都得
- 下一篇: 谈谈我理解的敏捷开发--转载