欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C# String字符串案例詳解

 更新時(shí)間:2021年07月08日 10:37:09   作者:蒼  
這篇文章主要介紹了C# String字符串案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

string是一種很特殊的數(shù)據(jù)類型,它既是基元類型又是引用類型,在編譯以及運(yùn)行時(shí),.Net都對(duì)它做了一些優(yōu)化工作,正式這些優(yōu)化工作有時(shí)會(huì)迷惑編程人員,使string看起來難以琢磨。這篇文章共四節(jié),來講講關(guān)于string的陌生一面。 

一.恒定的字符串

要想比較全面的了解stirng類型,首先要清楚.Net中的值類型與引用類型。

在C#中,以下數(shù)據(jù)類型為值類型: bool、byte、char、enum、sbyte以及數(shù)字類型(包括可空類型)
以下數(shù)據(jù)類型為引用類型: class、interface、delegate、object、stirng
看到了嗎,我們要討論的stirng赫然其中。被聲明為string型變量存放于堆中,是一個(gè)徹頭徹尾的引用類型。那么許多同學(xué)就會(huì)對(duì)如下代碼產(chǎn)生有疑問了,難道string類型也會(huì)“牽一發(fā)而動(dòng)全身”嗎?讓我們先來看看以下三行代碼有何玄機(jī):

string a = "str_1";
string b = a;
a = "str_2";

不要說無聊,這一點(diǎn)時(shí)必須講清楚的!在以上代碼中,第3行的“=”有一個(gè)隱藏的秘密:它的作用我們可以理解為新建,而不是對(duì)變量“a”的修改。以下是IL代碼,可以說明這一點(diǎn):

.maxstack  1
.locals init ([0] string a,[1] string b)
IL_0000:  nop
IL_0001:  ldstr      "str_1"
IL_0006:  stloc.0
IL_0007:  ldloc.0
IL_0008:  stloc.1
IL_0009:  ldstr      "str_2"
IL_000e:  stloc.0  //以上2行對(duì)應(yīng) C#代碼 a = "str_2";
IL_0015:  ret

  可以看出IL代碼的第1、6行,由ldstr指令創(chuàng)建字符串"str_1",并將其關(guān)聯(lián)到了變量“a”中;7、8行直接將堆棧頂部的值彈出并關(guān)聯(lián)到變量“b”中;9、10由ldstr創(chuàng)建字符串"str_2",關(guān)聯(lián)在變量“a”中(并沒有像我們想象的那樣去修改變量a的舊值,而是產(chǎn)生了新的字符串);

  在C#中,如果用new關(guān)鍵字實(shí)例化一個(gè)類,對(duì)應(yīng)是由IL指令newobj來完成的;而創(chuàng)建一個(gè)字符串,則由ldstr指令完成,看到ldstr指令,我們即可認(rèn)為,IL希望創(chuàng)建一個(gè)新的字符串 。(注意:是IL希望創(chuàng)建一個(gè)字符串,而最終是否創(chuàng)建,還要在運(yùn)行時(shí)由字符串的駐留機(jī)制決定,這一點(diǎn)下面的章節(jié)會(huì)有介紹。)

  所以,第三行C#代碼(a = "str_2";)的樣子看起來是在修改變量a的舊值"str_1",但實(shí)際上是創(chuàng)建了一個(gè)新的字符串"str_2",然后將變量a的指針指向了"str_2"的內(nèi)存地址,而"str_1"依然在內(nèi)存中沒有受到任何影響,所以變量b的值沒有任何改變---這就是string的恒定性,同學(xué)們,一定要牢記這一點(diǎn),在.Net中,string類型的對(duì)象一旦創(chuàng)建即不可修改!包括ToUpper、SubString、Trim等操作都會(huì)在內(nèi)存中產(chǎn)生新的字符串。

本節(jié)重點(diǎn)回顧:由于stirng類型的恒定性,讓同學(xué)友們經(jīng)常誤解,string雖屬引用類型但經(jīng)常表現(xiàn)出值的特性,這是由于不了解string的恒定性造成的,根本不是“值的特性”。例如:

string a = "str_1";
a = "str_2";

這樣會(huì)在內(nèi)存中創(chuàng)建"str_1"和"str_2"兩個(gè)字符串,但只有"str_2"在被使用,"str_1"不會(huì)被修改或消失,這樣就浪費(fèi)了內(nèi)存資源,這也是為什么在做大量字符串操作時(shí),推薦使用StringBuilder的原因。

二..Net中字符串的駐留(重要)

  在第一節(jié)中,我們講了字符串的恒定性,該特性又為我們引出了字符串的另一個(gè)重要特性:字符串駐留。
從某些方面講,正是字符串的恒定性,才造就了字符串的駐留機(jī)制,也為字符串的線程同步工作大開方便之門(同一個(gè)字符串對(duì)象可以在不同的應(yīng)用程序域中被訪問,所以駐留的字符串是進(jìn)程級(jí)的,垃圾回收不能釋放這些字符串對(duì)象,只有進(jìn)程結(jié)束這些對(duì)象才被釋放)。
我們用以下2行代碼來說明字符串的駐留現(xiàn)象:

string a = "str_1";
string b = "str_1";

  請(qǐng)各位同學(xué)友思考一下,這2行代碼會(huì)在內(nèi)存中產(chǎn)生了幾個(gè)string對(duì)象?你可能會(huì)認(rèn)為產(chǎn)生2個(gè):由于聲明了2個(gè)變量,程序第1行會(huì)在內(nèi)存中產(chǎn)生"str_1"供變量a所引用;第2行會(huì)產(chǎn)生新的字符串"str_1"供變量b所引用,然而真的是這樣嗎?我們用ReferenceEquals這個(gè)方法來看一下變量a與b的內(nèi)存引用地址:

string a = "str_1";
string b = "str_1";
Response.Write(ReferenceEquals(a,b));   //比較a與b是否來自同一內(nèi)存引用
//輸出:True

  哈,各位同學(xué)看到了嗎,我們用ReferenceEquals方法比較a與b,雖然我們聲明了2個(gè)變量,但它們竟然來自同一內(nèi)存地址!這說明string b = "str_1";根本沒有在內(nèi)存中產(chǎn)生新的字符串。

  這是因?yàn)?,?Net中處理字符串時(shí),有一個(gè)很重要的機(jī)制,叫做字符串駐留機(jī)制。由于string是編程中用到的頻率較高的一種類型,CLR對(duì)相同的字符串,只分配一次內(nèi)存。CLR內(nèi)部維護(hù)著一塊特殊的數(shù)據(jù)結(jié)構(gòu),我們叫它字符串池,可以把它理解成是一個(gè)HashTable,這個(gè)HashTable維護(hù)著程序中用到的一部分字符串,HashTable的Key是字符串的值,而Value則是字符串的內(nèi)存地址。一般情況下,程序中如果創(chuàng)建一個(gè)string類型的變量,CLR會(huì)首先在HashTable遍歷具有相同Hash Code的字符串,如果找到,則直接把該字符串的地址返回給相應(yīng)的變量,如果沒有才會(huì)在內(nèi)存中新建一個(gè)字符串對(duì)象。

  所以,這2行代碼只在內(nèi)存中產(chǎn)生了1個(gè)string對(duì)象,變量b與a共享了內(nèi)存中的"str_1"。

好了,結(jié)合第一節(jié)所講到的字符串恒定性與第二節(jié)所講到的駐留機(jī)制,來理解一下下面3行代碼吧:

string a = "str_1"; //聲明變量a,將變量a的指針指向內(nèi)存中新產(chǎn)生的"str_1"的地址
a = "str_2";  //CLR先會(huì)在字符串池中遍歷,查看"str_2"是否已存在,如果沒有,則新建"str_2",并修改變量a的指針,指向"str_2"內(nèi)存地址,"str_1"保持不變。(字符串恒定)
string c = "str_2"; //CLR先會(huì)在字符串池中遍歷"str_2"是否已存在,如果存在,則直接將變量c的指針指向"str_2"的地址。(字符串駐留)

那么如果是動(dòng)態(tài)創(chuàng)建字符串呢?字符串還會(huì)不會(huì)有駐留現(xiàn)象呢?

我們分3種情況講解動(dòng)態(tài)創(chuàng)建字符串時(shí),駐留機(jī)制的表現(xiàn):

(1).字符串常量連接

string a = "str_1" + "str_2";
string b = "str_1str_2";
Response.Write(ReferenceEquals(a,b));   //比較a與b是否來自同一內(nèi)存引用
//輸出 :True

IL代碼:

.maxstack  1
.locals init ([0] string a,[1] string b)
IL_0000:  nop
IL_0001:  ldstr      “str_1str_2”
IL_0006:  stloc.0
IL_0007:  ldstr      “str_1str_2”
IL_000c:  stloc.1
IL_000d:  ret

  其中第1、6行對(duì)應(yīng)c#代碼string a = “str_1” + “str_2”;第7、8對(duì)應(yīng)c# string b = “str_1str_2”;可以看出,字符串常量連接時(shí),程序在被編譯為IL代碼前,編譯器已經(jīng)計(jì)算出了字符串常量連接的結(jié)果,ldstr指令直接處理編譯器計(jì)算后的字符串值,所以這種情況字符串駐留機(jī)制有效!

(2).字符串變量連接

string a = "str_1";
string b = a + "str_2";
string c = "str_1str_2";
Response.Write(ReferenceEquals(b,c));
//輸出:False

IL代碼:

.maxstack  2
.locals init ([0] string a, [1] string b, [2] string c)
IL_0000:  nop
IL_0001:  ldstr      “str_1”
IL_0006:  stloc.0
IL_0007:  ldloc.0
IL_0008:  ldstr      “str_2”
IL_000d:  call       string [mscorlib]System.String::Concat(string,string)
IL_0012:  stloc.1
IL_0013:  ldstr      “str_1str_2”
IL_0018:  stloc.2
IL_0019:  ret

  其中第1、6行對(duì)應(yīng)string a = “str_1”;第7、8、9行對(duì)應(yīng)string b = a + “str_2”;,IL用的是Concat方法連接字符串,第13、18行對(duì)應(yīng)string c = “str_1str_2”;可以看出,字符串變量連接時(shí),IL使用Concat方法,在運(yùn)行時(shí)生成最終的連接結(jié)果,所以這種情況字符串駐留機(jī)制無效!

(3).顯式實(shí)例化

string a = "a";
string b = new string('a',1);
Response.Write(ReferenceEquals(a, b));
//輸出 False

IL代碼:

.maxstack  3
.locals init ([0] string a,[1] string b)
IL_0000:  nop
IL_0001:  ldstr      "a"
IL_0006:  stloc.0
IL_0007:  ldc.i4.s   97
IL_0009:  ldc.i4.1
IL_000a:  newobj     instance void [mscorlib]System.String::.ctor(char, int32)
IL_000f:  stloc.1
IL_0010:  ret

  這種情況比較好理解,IL使用newobj來實(shí)例化一個(gè)字符串對(duì)象,駐留機(jī)制無效。從string b = new string('a',1);這行代碼我們可以看出,其實(shí)string類型實(shí)際上是由char[]實(shí)現(xiàn)的,一個(gè)string的誕生絕不像我們想想的那樣簡(jiǎn)單,要由棧、堆同時(shí)配合,才會(huì)有一個(gè)string的誕生。這一點(diǎn)在第四節(jié)會(huì)有介紹。

當(dāng)然,當(dāng)字符串駐留機(jī)制無效時(shí),我們可以很簡(jiǎn)便的使用string.Intern方法將其手動(dòng)駐留至字符串池中,例如以下代碼:

string a = "a";
string b = new string('a',1);    
Response.Write(ReferenceEquals(a, string.Intern(b)));
//輸出:True (程序返回Ture,說明變量"a"與"b"來自同一內(nèi)存地址。)

三.有趣的比較操作

  在第一節(jié)與第二節(jié)中,我們分別介紹了字符串的恒定性與與駐留性,如果這位同學(xué)友覺得完全掌握了以上內(nèi)容,那么就在第三節(jié)中檢驗(yàn)一下自己的學(xué)習(xí)成果吧!以下10段簡(jiǎn)單的代碼將通過值比較與地址引用比較,來說明前兩節(jié)講到的內(nèi)容,大家也可以通過這些代碼來檢測(cè)一下自己對(duì)string的了解程度。

代碼一:

string a = "str_1";
string b = "str_1";
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a,b));

//輸出:True (Equals比較字符串對(duì)象的值)
//輸出:True (ReferenceEquals比較字符串對(duì)象的引用,由于字符串駐留機(jī)制,a與b的引用相同)

代碼二:

string a = "str_1str_2";
string b = "str_1";
string c = "str_2";
string d = b + c;
Response.Write(a.Equals(d));
Response.Write(ReferenceEquals(a, d));

//輸出:True (Equals比較字符串對(duì)象的值)
//輸出:False(ReferenceEquals比較字符串對(duì)象的引用,由于變量d的值為變量連接的結(jié)果,字符串駐留機(jī)制無效)

代碼三:

string a = "str_1str_2";
string b = "str_1" + "str_2";
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a, b));

//輸出:True (Equals比較字符串對(duì)象的值)
//輸出:True (ReferenceEquals比較字符串對(duì)象的引用,由于變量b的值為常量連接的結(jié)果,字符串駐留機(jī)制有效。如果變量b的值由“常量+變量”的方式得出,則字符串駐留無效)

代碼四:

string a = "str_1";
string b = String.Copy(a);
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a, b));

//輸出:True (Equals比較字符串對(duì)象的值)
//輸出:False(ReferenceEquals比較字符串對(duì)象的引用,Copy操作產(chǎn)生了新的string對(duì)象)

代碼五:

string a = "str_1";
string b = String.Copy(a);
b = String.Intern(b);
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a, b));

//輸出:True (Equals比較字符串對(duì)象的值)
//輸出:True (ReferenceEquals比較字符串對(duì)象的引用,String.Intern實(shí)現(xiàn)了字符串駐留)

代碼六:

string a = "str_1";
string b = String.Copy(a);
string c = "str_1";
Response.Write((object)a == (object)b);
Response.Write((object)a == (object)c);

//輸出:False(“==”在兩邊為引用類型時(shí),則比較引用的地址,所以a與b為不同引用)
//輸出:True (“==”在兩邊為引用類型時(shí),則比較引用的地址,所以a與c的引用相同)(原文:ReferenceEquals比較字符串對(duì)象的引用,a與c由于字符串駐留機(jī)制,引用相同)

代碼七:

string a = "str_1";
string c = "str_1";
Response.Write(a == c);

//輸出:True(剛才我們提到過,“==”在兩邊為引用類型時(shí),則比較引用的地址;如果是值類型時(shí)則需要比較引用和值。string為引用類型,那么上面的代碼是比較了變量a與c的地址還是地址和值呢?
       答案是:比較了地址和值!因?yàn)樵趕tring類型比較的時(shí)候,“==”已經(jīng)被重載為“Equals”了,所以,雖然你在用“==”比較兩個(gè)引用類型,但實(shí)際上是在用“Equals”比較它們的地址和值!(先比較地址,地址不等再比較值))

代碼八:

string a = "a";
string b = new string('a', 1);
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a, b));

//輸出:True (Equals比較值,a與b的值相同)
//輸出:False(ReferenceEquals比較字符串對(duì)象的引用)

代碼九:

string a = "a";
string b = new string('a', 1);
Response.Write(a.Equals(string.Intern(b)));
Response.Write(ReferenceEquals(a, string.Intern(b)));
//輸出:True (Equals比較值,無論是否Intern都會(huì)相同)
//輸出:True (ReferenceEquals比較字符串對(duì)象的引用,Intern已經(jīng)將b駐留至字符串池內(nèi))

代碼十:

string a = "str";
string b = "str_2".Substring(0,3);
Response.Write(a.Equals(b));
Response.Write(ReferenceEquals(a, b));
//輸出:True (Equals比較值,a與c的值相同)
//輸出:False(ReferenceEquals比較字符串對(duì)象的引用,Substring操作產(chǎn)生了新的字符串對(duì)象)

四.藝海拾貝

  這一節(jié)將主要給大家介紹一些string的常見問題。

(1)“string = ”與“new stirng()”的區(qū)別

string test = "a";
string test = new string('a', 1);

  以上兩行代碼的效果是一樣的,它們的區(qū)別在于加載”a”的時(shí)間不同:第一行的“a”是一個(gè)常量,在編譯期就已經(jīng)被放在一個(gè)叫做常量池的地方了,常量池通常裝載一些在編譯期被確定下來的數(shù)據(jù),例如類、接口等等;而第二行是運(yùn)行時(shí)CLR在堆中生成的值為“a”的字符串對(duì)象,所以后者沒有字符串駐留。

(2). string 與 String的區(qū)別

  String的大名叫做System.String,在編譯為IL代碼時(shí),string和System.String會(huì)生成完全相同的代碼:(ps:long和System.Int64,float和System.Single等也有此特性)

C#代碼:

string str_test = "test";
System.String Str_test = "test";

IL代碼:

// 代碼大小       14 (0xe)
.maxstack  1
.locals init ([0] string str_test,[1] string Str_test)
IL_0000:  nop
IL_0001:  ldstr      "test"
IL_0006:  stloc.0
IL_0007:  ldstr      "test"
IL_000c:  stloc.1
IL_000d:  ret

  所以,二者的區(qū)別并不在于底層,而是在于string是類似于int的基元類型;System. String是框架類庫(FCL)的基本類型,二者之間有直接的對(duì)應(yīng)關(guān)系。

(3).StringBuilder

  StringBuilder提供了高效創(chuàng)建字符串的方法,由StringBuilder表示的字符串是可變的(非恒定的),在需要多處使用“+”連接字符串變量的時(shí)候,推薦使用StringBuilder來完成,最后調(diào)用其ToString()方法輸出。當(dāng)調(diào)用了StringBuilder的ToString()方法之后,StringBuilder將返回其內(nèi)部維護(hù)的一個(gè)字符串字段引用,如再次修改StringBuilder,它將會(huì)創(chuàng)建一個(gè)新的字符串,這時(shí)被修改的是新的字符串,原來已經(jīng)返回的字符串才不會(huì)發(fā)生改變。

StringBuilder有兩個(gè)比較重要的內(nèi)部字段,大家需要掌握:

  m_MaxCapacity:StringBuilder的最大容量,它規(guī)定了最多可以放置到        

  m_StringValue的字符個(gè)數(shù),默認(rèn)值為Int32.MaxValue。m_MaxCapacity一旦被指定就不能再更改。

  m_StringValue:StringBuilder維護(hù)的一個(gè)字符數(shù)組串,實(shí)際上可以理解為一個(gè)字符串。StringBuilder重寫的Tostring()方法返回的就是這個(gè)字段。

到此這篇關(guān)于C# String字符串案例詳解的文章就介紹到這了,更多相關(guān)C# string字符串詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實(shí)現(xiàn)六大設(shè)計(jì)原則之里氏替換原則

    C#實(shí)現(xiàn)六大設(shè)計(jì)原則之里氏替換原則

    這篇文章介紹了C#實(shí)現(xiàn)六大設(shè)計(jì)原則之里氏替換原則的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-02-02
  • C# JsonHelper 操作輔助類,拿來直接用

    C# JsonHelper 操作輔助類,拿來直接用

    本文總結(jié)了一些常用的JSON操作輔助類,包括轉(zhuǎn)換、判斷、Ajax異步等操作,希望能幫到大家。
    2016-05-05
  • Unity3D 計(jì)時(shí)器的實(shí)現(xiàn)代碼(三種寫法總結(jié))

    Unity3D 計(jì)時(shí)器的實(shí)現(xiàn)代碼(三種寫法總結(jié))

    這篇文章主要介紹了Unity3D 計(jì)時(shí)器的實(shí)現(xiàn)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • C#串口通信工具類的封裝

    C#串口通信工具類的封裝

    這篇文章主要為大家詳細(xì)介紹了C#串口通信工具類封裝,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C#實(shí)現(xiàn)單例模式的多種方式

    C#實(shí)現(xiàn)單例模式的多種方式

    這篇文章介紹了C#實(shí)現(xiàn)單例模式的多種方式,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-01-01
  • C#同步網(wǎng)絡(luò)時(shí)間的方法實(shí)例詳解

    C#同步網(wǎng)絡(luò)時(shí)間的方法實(shí)例詳解

    這篇文章主要介紹了C#同步網(wǎng)絡(luò)時(shí)間的方法,以實(shí)例形式較為詳細(xì)的分析了C#獲取網(wǎng)絡(luò)時(shí)間與同步本機(jī)系統(tǒng)時(shí)間的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-05-05
  • C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼

    C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼

    這篇文章介紹了C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-01-01
  • C#實(shí)現(xiàn)rabbitmq 延遲隊(duì)列功能實(shí)例代碼

    C#實(shí)現(xiàn)rabbitmq 延遲隊(duì)列功能實(shí)例代碼

    本篇文章主要介紹了C#實(shí)現(xiàn)rabbitmq 延遲隊(duì)列功能實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • 使用Log4net進(jìn)行日志記錄

    使用Log4net進(jìn)行日志記錄

    本文詳細(xì)講解了使用Log4net進(jìn)行日志記錄的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • C#利用性能計(jì)數(shù)器監(jiān)控網(wǎng)絡(luò)狀態(tài)

    C#利用性能計(jì)數(shù)器監(jiān)控網(wǎng)絡(luò)狀態(tài)

    這篇文章主要為大家詳細(xì)介紹了C#利用性能計(jì)數(shù)器監(jiān)控網(wǎng)絡(luò)狀態(tài)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01

最新評(píng)論