国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

編寫(xiě)高質(zhì)量代碼改善C#程序的157個(gè)建議[正確操作字符串、使用默認(rèn)轉(zhuǎn)型方法、卻別對(duì)待強(qiáng)制轉(zhuǎn)換與as和is]

2019-11-17 03:15:53
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

編寫(xiě)高質(zhì)量代碼改善C#程序的157個(gè)建議[正確操作字符串、使用默認(rèn)轉(zhuǎn)型方法、卻別對(duì)待強(qiáng)制轉(zhuǎn)換與as和is]

前言

  本文主要來(lái)學(xué)習(xí)記錄前三個(gè)建議。

  建議1、正確操作字符串

  建議2、使用默認(rèn)轉(zhuǎn)型方法

  建議3、區(qū)別對(duì)待強(qiáng)制轉(zhuǎn)換與as和is

其中有很多需要理解的東西,有些地方可能理解的不太到位,還望指正。

建議1、正確操作字符串

  字符串應(yīng)該是所有編程語(yǔ)言中使用最頻繁的一種基礎(chǔ)數(shù)據(jù)類(lèi)型。如果使用不慎,我們就會(huì)為一次字符串的操作所帶來(lái)的額外性能開(kāi)銷(xiāo)而付出代價(jià)。本條建議將從兩個(gè)方面來(lái)探討如何規(guī)避這類(lèi)性能開(kāi)銷(xiāo):

  1、確保盡量少的裝箱

  2、避免分配額外的內(nèi)存空間

先來(lái)介紹第一個(gè)方面,請(qǐng)看下面的兩行代碼:

String str1="str1"+9;String str2="str2"+9.ToString();

從IL代碼可以得知,第一行代碼在運(yùn)行時(shí)完成一次裝箱的行為,而第二行代碼中并沒(méi)有發(fā)生裝箱的行為,它實(shí)際調(diào)用的是整型的ToString()方法,效率要比裝箱高。所以,在使用其他值引用類(lèi)型到字符串的轉(zhuǎn)換并完成拼接時(shí),應(yīng)當(dāng)避免使用操作符“+”來(lái)完成,而應(yīng)該使用值引用類(lèi)型提供的ToString()方法。

第二方面,避免分配額外的內(nèi)存空間。對(duì)CLR來(lái)說(shuō),string對(duì)象(字符串對(duì)象)是個(gè)很特殊的對(duì)象,它一旦被賦值就不可改變。在運(yùn)行時(shí)調(diào)用System.String類(lèi)中的任何方法或進(jìn)行任何運(yùn)算(如“=”賦值、“+”拼接等),都會(huì)在內(nèi)存中創(chuàng)建一個(gè)新的字符串對(duì)象,這也意味著要為該新對(duì)象分配新的內(nèi)存空間。像下面的代碼就會(huì)帶來(lái)運(yùn)行時(shí)的額外開(kāi)銷(xiāo)。

PRivate static void NewMethod1(){      string s1="abc";      s1="123"+s1+"456";    ////以上兩行代碼創(chuàng)建了3個(gè)字符串對(duì)象對(duì)象,并執(zhí)行了一次string.Contact方法            }private static void NewMethod2(){      string re=9+"456";    ////該方法發(fā)生了一次裝箱,并調(diào)用一次string.Contact方法  }

關(guān)于裝箱拆箱的問(wèn)題大家可以查看我之前的文章http://m.survivalescaperooms.com/aehyok/p/3504449.html

而以下代碼,字符串不會(huì)在運(yùn)行時(shí)進(jìn)行拼接,而是會(huì)在編譯時(shí)直接生成一個(gè)字符串。

private static void NewMethod3(){       string re2="123"+"abc"+"456";   ///該代碼等效于///string re2="123abc456";  }private static void NewMethod4(){      const string a="t";      string re="abc"+a;   ///因?yàn)閍是一個(gè)常量,所以該代碼等效于string=re="abc"+"t";  最終等效于string re="abct";  }  

由于使用System.String類(lèi)會(huì)在某些場(chǎng)合帶來(lái)明顯的性能損耗,所以微軟另外提供了一個(gè)類(lèi)型StringBuilder來(lái)彌補(bǔ)String的不足。

StringBuilder并不會(huì)重新創(chuàng)建一個(gè)string對(duì)象,它的效率源于預(yù)先以非托管的方式分配內(nèi)存。如果StringBuilder沒(méi)有先定義長(zhǎng)度,則默認(rèn)分配的長(zhǎng)度為16。當(dāng)StringBuilder字符串長(zhǎng)度小于等于16時(shí),StringBuilder不會(huì)重新分配內(nèi)存;當(dāng)StringBuilder字符長(zhǎng)度大于16小于32時(shí),StringBuilder又會(huì)重新分配內(nèi)存,使之成為16的倍數(shù)。在上面的代碼中,如果預(yù)先判斷字符串的長(zhǎng)度將大于16,則可以為其設(shè)定一個(gè)更加合適的長(zhǎng)度(如32)。StringBuilder重新分配內(nèi)存時(shí)是按照上次容量加倍進(jìn)行分配的。當(dāng)然,我們需要注意,StringBuilder指定的長(zhǎng)度要合適,太小了,需要頻繁分配內(nèi)存,太大了,浪費(fèi)空間。

查看以下代碼,比較下面兩種字符串拼接方式,哪種效率更高:

        private static void NewMethod1()        {            string a = "t";            a += "e";            a += "s";            a += "t";        }        private static void NewMethod2()        {            string a = "t";            string b = "e";            string c = "s";            string d = "t";            string result = a + b + c + d;        }

  結(jié)果可以得知:兩者的效率都不高。不要以為前者比后者創(chuàng)建的字符串對(duì)象更少,事實(shí)上,兩者創(chuàng)建的字符串對(duì)象相等,且前者進(jìn)行了3次string.Contact方法調(diào)用,比后者還多了兩次。

  要完成這樣的運(yùn)行時(shí)字符串拼接(注意:是運(yùn)行時(shí)),更佳的做法是使用StringBuilder類(lèi)型,代碼如下所示:

        public static void NewMethod()        {            ////定義了四個(gè)變量            string a = "t";            string b = "e";            string c = "s";            string d = "t";            StringBuilder sb = new StringBuilder(a);            sb.Append(b);            sb.Append(c);            sb.Append(d);                        ///提示是運(yùn)行時(shí),所以沒(méi)有使用以下代碼            //StringBuilder sb = new StringBuilder("t");            //sb.Append("e");            //sb.Append("s");            //sb.Append("t");            //string result = sb.ToString();        }

微軟還提供了另外一個(gè)方法來(lái)簡(jiǎn)化這種操作,即使用string.Format方法。string.Format方法在內(nèi)部使用StringBuilder進(jìn)行字符串的格式化,代碼如下所示:

        public static void NewMethod4()        {            string a = "t";            string b = "e";            string c = "s";            string d = "t";            string result = string.Format("{0}{1}{2}{3}", a, b, c, d);        }

對(duì)于String和StringBuilder的簡(jiǎn)單介紹也可以參考我之前的一篇文章http://m.survivalescaperooms.com/aehyok/p/3505000.html

建議2、使用默認(rèn)轉(zhuǎn)型方法

1、使用類(lèi)型的轉(zhuǎn)換運(yùn)算符,其實(shí)就是使用類(lèi)型內(nèi)部的一方方法(即函數(shù))。轉(zhuǎn)換運(yùn)算符分為兩類(lèi):隱式轉(zhuǎn)換和顯式轉(zhuǎn)換(強(qiáng)制轉(zhuǎn)換)?;?lèi)型普遍都提供了轉(zhuǎn)換運(yùn)算符。

所謂“基元類(lèi)型”,是指編譯器直接支持的數(shù)據(jù)類(lèi)型。基元類(lèi)型包括:sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、bool、decimal、object、string。

            int i = 0;            float j = 0;            j = i;  ///int 到float存在一個(gè)隱式轉(zhuǎn)換            i = (int)j; ///float到int必須存在一個(gè)顯式轉(zhuǎn)換

用戶(hù)自定義的類(lèi)型也可以通過(guò)重載轉(zhuǎn)換運(yùn)算符的方式提供這一類(lèi)轉(zhuǎn)換:

    public class ip    {        IPAddress value;        public Ip(string ip)        {            value = IPAddress.Parse(ip);        }        //重載轉(zhuǎn)換運(yùn)算符,implicit 關(guān)鍵字用于聲明隱式的用戶(hù)定義類(lèi)型轉(zhuǎn)換運(yùn)算符。        public static implicit Operator Ip(string ip)        {            Ip iptemp = new Ip(ip);            return iptemp;        }        //重寫(xiě)ToString方法        public override string ToString()        {            return value.ToString();        }    }    class Program    {        public static void Main(string[] args)        {            Ip ip = "192.168.1.1";   //通過(guò)Ip類(lèi)的重載轉(zhuǎn)換運(yùn)算符,實(shí)現(xiàn)字符串到Ip類(lèi)型的隱式轉(zhuǎn)換            Console.WriteLine(ip.ToString());            Console.ReadLine();        }    }

提供的就是字符串到類(lèi)型Ip之間的隱式轉(zhuǎn)換。

2、使用類(lèi)型內(nèi)置的Parse、TryParse,或者如ToString、ToDouble、ToDateTime等方法

比如從string轉(zhuǎn)換為int,因?yàn)槠浣?jīng)常發(fā)生,所以int本身就提供了Parse和TryParse方法。一般情況下,如果要對(duì)某類(lèi)型進(jìn)行轉(zhuǎn)換操作,建議先查閱該類(lèi)型的API文檔。

3、使用幫助類(lèi)提供的方法

可以使用System.Convert類(lèi)、System.BitConverter類(lèi)來(lái)進(jìn)行類(lèi)型的轉(zhuǎn)換。

System.Convert提供了將一個(gè)基元類(lèi)型轉(zhuǎn)換為其他基元類(lèi)型的方法,如ToChar、ToBoolean方法等。值得注意的是,System.Convert還支持將任何自定義類(lèi)型轉(zhuǎn)換為任何基元類(lèi)型,只要自定義類(lèi)型繼承了IConvertible接口就可以。如上文中的IP類(lèi),如果將Ip轉(zhuǎn)換為string,除了重寫(xiě)Object的ToString方法外,還可以實(shí)現(xiàn)IConvertible的ToString()方法

繼承IConvertible接口必須同時(shí)實(shí)現(xiàn)其他轉(zhuǎn)型方法,如上文的ToBoolean、ToByte,如果不支持此類(lèi)轉(zhuǎn)型,則應(yīng)該拋出一個(gè)InvalidCastException,而不是一個(gè)NotImplementedException。

4、使用CLR支持的轉(zhuǎn)型

CLR支持的轉(zhuǎn)型,即上溯轉(zhuǎn)型和下溯轉(zhuǎn)型。這個(gè)概念首先是在java中提出來(lái)的,實(shí)際上就是基類(lèi)和子類(lèi)之間的相互轉(zhuǎn)換。

就比如: 動(dòng)作Animal類(lèi)、Dog類(lèi)繼承Animal類(lèi)、Cat類(lèi)也繼承自Amimal類(lèi)。在進(jìn)行子類(lèi)向基類(lèi)轉(zhuǎn)型的時(shí)候支持隱式轉(zhuǎn)換,如Dog顯然就是一個(gè)Animal;而當(dāng)Animal轉(zhuǎn)型為Dog的時(shí)候,必須是顯式轉(zhuǎn)換,因?yàn)锳nimal還可能是一個(gè)Cat。

            Animal animal = new Animal();            Dog dog = new Dog();            animal = dog;   /////隱式轉(zhuǎn)換,因?yàn)镈og就是Animal            ///dog=animal;  ////編譯不通過(guò)            dog = (dog)animal; /////必須存在一個(gè)顯式轉(zhuǎn)換

建議3、區(qū)別對(duì)待強(qiáng)制轉(zhuǎn)換與as和is

首先來(lái)看一個(gè)簡(jiǎn)單的實(shí)例

            FirstType firstType = new FirstType();            SecondType secondType = new SecondType();            secondType = (SecondType)firstType;

從上面的三行代碼可以看出,類(lèi)似上面的應(yīng)該就是強(qiáng)制轉(zhuǎn)換。

首先需要明確強(qiáng)制轉(zhuǎn)換可能意味這兩件不同的事情:

1、FirstType和SecondType彼此依靠轉(zhuǎn)換操作來(lái)完成兩個(gè)類(lèi)型之間的轉(zhuǎn)換。

2、FirstType是SecondType的基類(lèi)。

類(lèi)型之間如果存在強(qiáng)制轉(zhuǎn)換,那么它們之間的關(guān)系要么是第一種,要么是第二種。不可能同時(shí)是繼承的關(guān)系,又提供了轉(zhuǎn)型符。

針對(duì)第一種情況:

    public class FirstType    {        public string Name { get; set; }    }    public class SecondType    {        public string Name { get; set; }        public static explicit operator SecondType(FirstType firstType)        {            SecondType secondType = new SecondType() { Name = "轉(zhuǎn)型自:" + firstType.Name };            return secondType;        }    }    class Program    {        static void Main(string[] args)        {            FirstType firstType = new FirstType() { Name="First Type"};            SecondType secondType = (SecondType)firstType;  ///此轉(zhuǎn)換是成功的            secondType = firstType as SecondType;           ///編譯不通過(guò)            Console.ReadLine();        }    }

這里上面也有添加注釋?zhuān)ㄟ^(guò)強(qiáng)制轉(zhuǎn)換是可以轉(zhuǎn)換成功的,但是使用as運(yùn)算符是不成功的編譯就不通過(guò)。

這里就是通過(guò)轉(zhuǎn)換符進(jìn)行處理的結(jié)果。

接下來(lái)我們?cè)僭赑rogram類(lèi)中添加一個(gè)方法

        static void DoWithSomeType(object obj)        {            ///編譯器首先判斷的是,SEOndType和ojbect之間有沒(méi)有繼承關(guān)系。            ///因?yàn)樵贑#中,所有的類(lèi)型都是繼承自object的,所以這里編譯沒(méi)有什么問(wèn)題。            ///但編譯器會(huì)自動(dòng)產(chǎn)生代碼來(lái)檢查obj在運(yùn)行時(shí)是不是SecondType,這樣就繞過(guò)了操作轉(zhuǎn)換符,導(dǎo)致轉(zhuǎn)換失敗。            SecondType secondType = (SecondType)obj;        }

如注釋所說(shuō)的,編譯通過(guò)執(zhí)行報(bào)錯(cuò)的問(wèn)題。

如果類(lèi)型之間都上溯到了某個(gè)共同的基類(lèi),那么根據(jù)此基類(lèi)進(jìn)行的轉(zhuǎn)換(即基類(lèi)轉(zhuǎn)型為子類(lèi)本身),應(yīng)該使用as。子類(lèi)與子類(lèi)之間的轉(zhuǎn)換,則應(yīng)該提供轉(zhuǎn)換操作符,以便進(jìn)行強(qiáng)制轉(zhuǎn)換。

現(xiàn)在可以如上方法改寫(xiě)為

        static void DoWithSomeType(object obj)        {            SecondType secondType = obj as SecondType;        }

保證編譯執(zhí)行都不會(huì)報(bào)錯(cuò)。as操作符永遠(yuǎn)不會(huì)拋出異常,如果類(lèi)型不匹配(被轉(zhuǎn)換對(duì)象的運(yùn)行時(shí)類(lèi)型既不是所轉(zhuǎn)換的目標(biāo)類(lèi)型,也不是其派生類(lèi)型),或者轉(zhuǎn)型的源對(duì)象為null,那么轉(zhuǎn)型之后的值也為null。改造前的Do

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阳泉市| 新河县| 宁强县| 肇源县| 长岛县| 辽宁省| 钟祥市| 永嘉县| 专栏| 兴安盟| 闵行区| 东至县| 毕节市| 平乐县| 仁怀市| 百色市| 新民市| 大理市| 公主岭市| 巴楚县| 讷河市| 郯城县| 边坝县| 庆阳市| 黔西| 阳泉市| 大港区| 柘城县| 定兴县| 科尔| 旅游| 凭祥市| 无为县| 同心县| 柳州市| 贡山| 定结县| 德江县| 仁化县| 贵州省| 绵阳市|