深入理解C#:編程技巧總結(jié)(一)

具體轉(zhuǎn)載地址見最下文

以下總結(jié)參閱了:MSDN文檔奶陈、《C#高級編程》易阳、《C#本質(zhì)論》、前輩們的博客等資料吃粒,如有不正確的地方潦俺,請幫忙及時(shí)指出!以免誤導(dǎo)徐勃!

1..實(shí)現(xiàn)多態(tài)性的兩種方式:繼承抽象類事示、實(shí)現(xiàn)接口

其實(shí)就是協(xié)變的應(yīng)用,通過把對象向上轉(zhuǎn)型為基類或接口類型僻肖,對它調(diào)用成員肖爵,可實(shí)現(xiàn)多態(tài)性,即運(yùn)行時(shí)調(diào)用的是對應(yīng)對象的實(shí)現(xiàn)版本成員臀脏。這兩種方式的區(qū)別:

繼承抽象類:會用掉唯一1次的繼承機(jī)會劝堪,但可以繼承任何成員(包括字段),自由度高

實(shí)現(xiàn)接口:必須實(shí)現(xiàn)所有成員揉稚,不能包含字段秒啦,但可以實(shí)現(xiàn)多個(gè)接口

抽象類可以提供成員的具體實(shí)現(xiàn),而接口只負(fù)責(zé)聲明搀玖,不能提供任何實(shí)現(xiàn)代碼

注意:

接口一旦被定義就不應(yīng)該再被改變余境,否則所有實(shí)現(xiàn)該接口的類型都必須跟著修改。

而抽象類則可以隨時(shí)添加新的成員灌诅,不影響他的子類芳来,還能提供新的額外功能。

多態(tài)性示例:(協(xié)變與逆變)

//可以返回Stream的任何子類類型

Stream Method1(bool boo)

{ }

//可以接收Stream的任何子類類型的參數(shù)

void Method2(Stream stream)

{ }

2.不要?jiǎng)?chuàng)建可變的值類型(結(jié)構(gòu)猜拾、枚舉)绣张,若要改變,請用一個(gè)方法來返回一個(gè)新實(shí)例关带。要時(shí)刻注意頻繁的裝箱與拆箱對性能的影響

3.僅在能一眼看出變量的類型時(shí),才使用var聲明

4.定義值類型時(shí)沼撕,它的大小不要超過16字節(jié)宋雏,否則影響性能(頻繁復(fù)制時(shí)),要么改為使用引用類型务豺,要么讓它按ref引用傳遞

5.值類型數(shù)組之間不能直接互相轉(zhuǎn)換磨总,可以通過一次中間轉(zhuǎn)換為Array來達(dá)到目的,如:

(int[])(Array)new uint[32]

但應(yīng)注意可能在不同的CLR實(shí)現(xiàn)中表現(xiàn)不同笼沥!

6.數(shù)組與List

如果元素?cái)?shù)量固定蚪燕,且不涉及轉(zhuǎn)型娶牌,則使用數(shù)組效率更高。

在元素?cái)?shù)量可能發(fā)生變化的情況下馆纳,就不應(yīng)該使用數(shù)組诗良,而應(yīng)該使用List

無論是數(shù)組還是List,元素個(gè)數(shù)也不能太多鲁驶,避免成為占用內(nèi)存超過85000字節(jié)的大對象鉴裹,因?yàn)榇髮ο髮环峙涞絾为?dú)的堆進(jìn)行處理,在回收大對象時(shí)效率較低钥弯。

7.字符串操作

字符串字面量径荔、字符串常量,直接用"+"相連效率高脆霎,因?yàn)椋簊tring str = "srf"+"ttt"+"ccc";會直接編譯成string str = "srftttccc";总处,同樣適用于字符串常量。

盡量避免對變量的裝箱:字符串+變量睛蛛,較好的做法是:字符串+變量.ToString()

頻繁操作字符串時(shí)用StringBuilder鹦马,并制定足夠大的容量,而string.Format("{0}{1}{2}",str1,str2,str3);內(nèi)部也是用StringBuilder玖院。

8.類型轉(zhuǎn)換

字符串轉(zhuǎn)其它基元類型:

默認(rèn)十進(jìn)制:用Parse()菠红、TryParse(),如:int.TryParse("24");难菌,其中TryParse效率更高

指定基數(shù)進(jìn)制形式來解析:Convert.ToInt32("0xFF",16);

從字節(jié)數(shù)組中提取一段试溯,轉(zhuǎn)為基元類型:BitConvert.ToInt32(Byte[] arr, int startIndex);

自定義類型之間的強(qiáng)制轉(zhuǎn)換:

從基類強(qiáng)制轉(zhuǎn)換為子類時(shí),安全的做法是使用"as"郊酒,若目標(biāo)為null或類型不兼容轉(zhuǎn)換失敗遇绞,均會返回null,而不會引發(fā)錯(cuò)誤燎窘,如基類Person摹闽,它的子類Man、Women

Person person = new Man();//自動(dòng)向基類隱式轉(zhuǎn)換褐健,但person的運(yùn)行時(shí)類型仍為Man

Women women = (Women)person; //錯(cuò)誤

Women women = person as Women; //women為null 付鹿,因?yàn)槟腥瞬荒苻D(zhuǎn)換為女人

但需注意"as"只能應(yīng)用于引用類型或可為null類型。若目標(biāo)可能為基元類型蚜迅,則應(yīng)該通過"is"操作符來過濾

if(!(person is int))

{

? ?Women women = person as Women;

}

子類與子類之間的橫向轉(zhuǎn)換舵匾,應(yīng)該定義轉(zhuǎn)換操作符(關(guān)鍵字implicit、explicit)

9.獲取一個(gè)可空類型Nullable的值谁不,安全簡單的做法是用"??"坐梯,如 int j = i ?? 0;,普通做法:

if(i.HasValue()) { int j = i.Value; }

10.常量const和只讀字段readonly的區(qū)別:

const是編譯期常量刹帕,它總是靜態(tài)的吵血,編譯時(shí)直接用實(shí)際值填充谎替。而readonly是一個(gè)運(yùn)行時(shí)常量。

const只能修飾基元類型蹋辅、枚舉類型钱贯、字符串類型,而readonly沒有限制晕翠。

const一經(jīng)聲明就必須初始化喷舀,且之后就無法再改變。而readonly可顯式初始化淋肾,也可不初始化硫麻,它的值可以通過構(gòu)造函數(shù)來改變(即每個(gè)實(shí)例有自己的readonly只讀字段值)

注意:除了構(gòu)造函數(shù)之外,都無法改變r(jià)eadonly的值樊卓,對于引用類型是無法改變它的引用拿愧,即它只能引用同一對象。但該對象本身是可以被修改的碌尔。

11.枚舉類型

枚舉類型可以為從byte到ulong的基元類型浇辜,定義枚舉時(shí)應(yīng)該始終為它定義一個(gè)零值,因?yàn)槁暶饕粋€(gè)枚舉變量而未初始化時(shí)的默認(rèn)值將是0

除了0值唾戚,要么都不為成員顯式賦值柳洋,要么就全部賦值(如應(yīng)用了Flags特性的標(biāo)志枚舉),否則未賦值的成員將等于它前一個(gè)成員的值加1叹坦,因?yàn)槊杜e成員的值默認(rèn)是按順序逐個(gè)加1

對枚舉應(yīng)用[Flags]特性熊镣,可以定義一個(gè)標(biāo)志枚舉,它的成員值通常初始化為2的次冪募书,之后就可以通過按位運(yùn)算來判斷绪囱、合并枚舉成員了。

定義一個(gè)枚舉來專門負(fù)責(zé)表示狀態(tài)的信息莹捡,這樣使代碼更易理解鬼吵。如用枚舉成員on、off來代替true篮赢、false或0齿椅、1

12.如果需要,應(yīng)該為類型重載常用的運(yùn)算符和比較運(yùn)算符启泣,如重載">"以實(shí)現(xiàn)person1>person2

13.若該類型有泛型版本媒咳,則應(yīng)該使用泛型版本,因?yàn)榉盒皖愋托矢?避免了裝箱种远、拆箱、類型轉(zhuǎn)換)

14.相等性

值類型:對于值相等的兩個(gè)值類型變量A顽耳、B坠敷,"A==B"和"A.Equals(B)"都返回true妙同,而Object.ReferenceEquals(A,B)總是返回false。

引用類型:Object.ReferenceEquals(A,B)比較的是引用是否相等膝迎,而默認(rèn)的A.Equals(B)也是比較的引用粥帚,需要重載Equals()方法來實(shí)現(xiàn)引用類型之間的"值相等性比較"(如:當(dāng)person1.ID == person2.ID時(shí),person1.Equals(person2)返回true限次,來表示他們相等)

注意1:重寫了Equals()方法芒涡,最好也一起重寫GetHashCode()方法,因?yàn)閷τ诓煌膶ο舐袈J(rèn)的GetHashCode()返回的值將永遠(yuǎn)不同费尽,而若把對象作為Dictionary的TKey時(shí),根據(jù)TKey取值時(shí)羊始,會根據(jù)對象的HashCode來比較旱幼。所以需要重新GetHashCode(),使得Equals()方法返回true時(shí)突委,GetHashCode()返回的值也相同柏卤,這樣字典才能正常工作。

注意2:重寫了Equals()匀油、GetHashCode()方法缘缚,同時(shí)也應(yīng)該實(shí)現(xiàn)IEquatable接口,該接口的成員bool Equals(T t1)比Object的Equals(object obj)類型更安全敌蚜、更高效桥滨。

注意3:對于字符串,雖然它也是對象钝侠,但當(dāng)兩個(gè)字符串所包含的字面值一樣時(shí)该园,運(yùn)行時(shí)將只在內(nèi)存中創(chuàng)建一個(gè)該字面值的字符串對象,也就是說所有字面值一樣的字符串對象都將引用同一個(gè)地址帅韧。

15.ToString()方法

應(yīng)該總是為自定義類型重寫Object的ToString()方法里初,最好還要實(shí)現(xiàn)IFormattable接口,該接口的ToString(string format, IFormatProvider formatProvider)提供了根據(jù)參數(shù)來輸出特定的格式化形式忽舟。如:

public string ToString(string format, IFormatProvider formatProvider)

{

? ?switch(format)

? ?{

? ? ? ?case "CH":

? ? ? ? ? ? ? ?return this.ToString();

? ? ? ?case "EN":

? ? ? ? ? ? ? ?return string.Format("{0}{1}",FirstName,LastName);

? ? ? ?......

? ?}

}

//調(diào)用

Console.WriteLine(person.ToString("EN",null));

16.對象的淺拷貝與深拷貝

淺拷貝:使用Object基類的實(shí)例方法MemberwiseClone()來獲得對象的一個(gè)淺拷貝副本双妨。

深拷貝:通過系列化與反系列化來深拷貝一個(gè)對象。

通常做法叮阅,如下:接口ICloneable唯一成員是object Clone()刁品,實(shí)現(xiàn)該接口只是為了表明該類型的實(shí)現(xiàn)可以被拷貝

[Serializable]

class Person : ICloneable

{

public string ID {get;set;}

public int Age {get;set;}

public Work work {get;set;}

//實(shí)現(xiàn)ICloneable接口的Clone()

public object Clone()

{

return this.MemberwiseClone();

}

//自定義深拷貝方法

public Person DeepClone()

{

using (Stream objectStream = new MemoryStream())

{

IFormatter formatter = new BinaryFormatter();

formatter.Serialize(objectStream, this);

objectStream.Seek(0, SeekOrigin.Begin);

return formatter.Deserialize(objectStream) as Person;

}

}

}

17.集合的遍歷

for循環(huán):采用索引器,for循環(huán)的優(yōu)點(diǎn)是遍歷過程中可以修改集合的元素浩姥。

foreach循環(huán):采用迭代器挑随,遍歷過程中無法對集合增刪元素操作,因?yàn)榈髦粚υ及姹镜募线M(jìn)行遍歷勒叠,每次迭代都會進(jìn)行版本判斷兜挨,若集合發(fā)生變化锌云,將拋出異常研铆。- - - - foreach循環(huán)的優(yōu)點(diǎn)是語法更簡潔宪萄,且迭代完畢后自動(dòng)調(diào)用Dispose()(foreach循環(huán)內(nèi)部使用了try...finally)

18.選擇正確的集合:詳解請參見《C#高級編程》谱净,書中對集合講的很細(xì)

線性:集合的每個(gè)元素都是是1對1的,大部分常用集合都是線性集合

非線性:1對多噪舀、多對1魁淳、多對多(樹、集HashSet与倡、圖)

直接存冉绻洹:具有索引器,元素按索引器排列蒸走,訪問仇奶、查找速度快,在末尾添加刪除速度也快比驻,但在中間刪除该溯、插入元素效率低(需要移動(dòng)后面的所有元素)。(數(shù)組别惦、List狈茉、字符串、結(jié)構(gòu))

順序存鹊УА:即線性表氯庆,可動(dòng)態(tài)擴(kuò)大或縮小,通過對地址的引用來搜索元素扰付,刪除堤撵、插入元素效率高,但查找效率低(需要遍歷查找)(Stack羽莺、Queue实昨、Dictionary、LinkedList等)

多線程集合類:位于System.Collections.Concurrent命名空間中盐固,如ConcurrentBag對應(yīng)于List荒给、ConcurrentDictionary、ConcurrentStack刁卜、ConcurrentQueue

實(shí)現(xiàn)自定義集合類時(shí)志电,不要繼承自內(nèi)置的集合類,而應(yīng)該自行實(shí)現(xiàn)相應(yīng)的泛型接口:

IEnumerable:提供迭代功能

ICollection:提供常用操作

IList

19.泛型

避免為自定義泛型定義靜態(tài)成員蛔趴,在不同的類型之間共享靜態(tài)成員沒意義挑辆。

記得為泛型參數(shù)設(shè)定必要的約束,因?yàn)榧s束之后可以使泛型參數(shù)成為一個(gè)實(shí)實(shí)在在的"對象",可以訪問到約束類型的實(shí)例成員之拨,而不做約束的話僅僅是一個(gè)object對象

必要時(shí)用default(T)為泛型類型變量指定默認(rèn)值茉继,如T param = default(T);

20.委托

預(yù)定義的委托類型能滿足大部分日常需求,我們沒有必要聲明自己的委托類型蚀乔。

Action,Action:接受0個(gè)或多個(gè)輸入?yún)?shù)菲茬,無返回值

Func吉挣,F(xiàn)unc:接受0個(gè)或多個(gè)輸入?yún)?shù),帶返回值婉弹,類型是TResult

Predicate:表示定義一組條件并判斷參數(shù)是否符合條件

具有特定用途的委托:

事件委托:

public delegate void EventHandler(object sender, EventArgs e);

public delegate void EventHandler(object sender, TEventArgs e);

線程中的委托:

public delegate void ThreadStart(); //無參數(shù)

public delegate void ParameterrizedThreadStart(object obj); //參數(shù)對象obj

異步回調(diào)委托:

public delegate void AsyncCallback(IAsyncResult ar);

21.對于只用一次睬魂,且主體語句數(shù)量較少的方法,應(yīng)該使用Lambda表達(dá)式镀赌,它通常用于注冊給委托氯哮、或作為其它方法的參數(shù)(參數(shù)類型是匹配的委托類型)

22.理解委托的本質(zhì):

委托是一個(gè)類

委托保存著對注冊方法的引用(方法指針),多播委托保存著一組方法指針

執(zhí)行委托商佛,將按順序調(diào)用方法指針指向的方法

對一個(gè)委托實(shí)例用"="賦值一個(gè)新的方法指針時(shí)喉钢,將會調(diào)用構(gòu)造函數(shù)實(shí)例化一個(gè)新的委托對象

所以在實(shí)例化一個(gè)委托對象之后后,應(yīng)該時(shí)刻記住使用"+="良姆、"-="來增加肠虽、刪除新的方法指針

委托類的方法:Invoke()默認(rèn)調(diào)用、在線程池中啟用一個(gè)新線程調(diào)用BeginInvoke()玛追、停止EndInvoke()

23.事件也是委托税课,加了event關(guān)鍵字是為了限制委托:

禁止了在包含類外部對委托事件對象使用"="賦值,確保不會被覆蓋或賦值為null

禁止了在包含類外部對委托事件對象的直接調(diào)用痊剖,事件的調(diào)用應(yīng)該是包含類的責(zé)任

參數(shù)1是觸發(fā)者對象的引用韩玩,參數(shù)2是EventArgs或其派生類的對象(可包含一些將在事件觸發(fā)時(shí)需要用到的數(shù)據(jù))

24.當(dāng)委托和Lambda小心閉包對象

(特別是在循環(huán)體中的循環(huán)變量,對于C#5.0的foreach則不必?fù)?dān)心)

當(dāng)Lambda表達(dá)式引用了局部變量時(shí)陆馁,編譯器就會自動(dòng)創(chuàng)建一個(gè)閉包對象(如TempClass)找颓,該對象的成員包含一個(gè)對局部變量的引用(如TempClass.i)、和一個(gè)與Lambda表達(dá)式等價(jià)的方法(如TempClass.add氮惯,該方法持有對局部變量的引用)叮雳。

而該閉包對象中的方法成員TempClass.add最終被賦給了委托(如MyDel),而委托通常在局部變量的作用域之外才執(zhí)行妇汗。

也就是說帘不,委托中注冊的方法持有了對局部變量的引用,形成了像JavaScript中的閉包一樣的效果杨箭,執(zhí)行委托方法時(shí)寞焙,局部變量的值將是最新值,而不是給委托注冊方法時(shí)的局部變量值。

public static void Main()

{

? ?Action act=new Action(()=>Console.WriteLine("Begin"));

? ?for (int i = 0; i < 5; i++)

? ?{

? ? ? ?act += () => Console.WriteLine(i.ToString());

? ?}

? ?act(); //Begin 5 5 5 5 5 ?因?yàn)槲蟹椒ǔ钟辛藢的引用捣郊,當(dāng)前i的值為5

? ?Console.ReadKey();

}

public static void Main()

{

? ?Action act=new Action(()=>Console.WriteLine("Begin"));

? ?for (int i = 0; i < 5; i++)

? ?{

? ? ? ?int temp = i; //每次都用一個(gè)新的temp變量來保存當(dāng)前的i值

? ? ? ?act += () => Console.WriteLine(temp.ToString());

? ?}

? ?act(); //Begin 0 1 2 3 4

? ?Console.ReadKey();

}

25.賦值為null辽狈,大部分情況下不能提前垃圾回收。

沒有必要將沒用的實(shí)例成員顯式賦值為null呛牲,因?yàn)榫幾g器會忽略該語句刮萌。

只有對日后確實(shí)沒用的靜態(tài)字段顯式賦值為null才有必要,但要確保不會再用到它(或者說不會再用到它的包含類)娘扩。

把一個(gè)對象賦值為null着茸,它的靜態(tài)成員不會跟著變?yōu)閚ull,因?yàn)殪o態(tài)成員跟類的實(shí)例無關(guān)琐旁,它會一直留在內(nèi)存中涮阔,除非顯式賦值為null。

后續(xù)還有很多其它方面的灰殴,如系列化與反系列化敬特,異常處理等,由于篇幅有限牺陶,只能等下一篇再發(fā)布了

------------------------------------

原文地址:http://www.cnblogs.com/susufufu/p/6263122.html

? ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伟阔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子义图,更是在濱河造成了極大的恐慌减俏,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碱工,死亡現(xiàn)場離奇詭異娃承,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)怕篷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門历筝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人廊谓,你說我怎么就攤上這事梳猪。” “怎么了蒸痹?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵春弥,是天一觀的道長。 經(jīng)常有香客問我叠荠,道長匿沛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任榛鼎,我火速辦了婚禮逃呼,結(jié)果婚禮上鳖孤,老公的妹妹穿的比我還像新娘。我一直安慰自己抡笼,他們只是感情好苏揣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著推姻,像睡著了一般平匈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藏古,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天吐葱,我揣著相機(jī)與錄音,去河邊找鬼校翔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灾前,可吹牛的內(nèi)容都是我干的防症。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哎甲,長吁一口氣:“原來是場噩夢啊……” “哼蔫敲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起炭玫,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤奈嘿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吞加,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裙犹,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年衔憨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叶圃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡践图,死狀恐怖掺冠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情码党,我是刑警寧澤德崭,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站揖盘,受9級特大地震影響眉厨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扣讼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一缺猛、第九天 我趴在偏房一處隱蔽的房頂上張望缨叫。 院中可真熱鬧,春花似錦荔燎、人聲如沸耻姥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琐簇。三九已至,卻和暖如春座享,著一層夾襖步出監(jiān)牢的瞬間婉商,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工渣叛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丈秩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓淳衙,卻偏偏與公主長得像蘑秽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子箫攀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容