CLR via C#學習筆記-第十四章-字元和字串
14.1 字元
Char結構
Char結構提供的欄位
每個字元都是System.Char結構的例項,Char型別提供了兩個公共只讀常量欄位:MinValue('\0')和MaxValue('\uffff\)。
Char例項能呼叫的方法
為Char的例項呼叫靜態GetUnicodeCategory方法返回System.Globalization.UnicodeCategory列舉型別的一個值,表明該字元是由Unicode標準定義的什麼字元。
Char型別提供了幾個了靜態方法,大多數都在內部呼叫了GetUnicodeCategory方法,並簡單的返回true、false。
Char可以轉成數值
轉型(強制型別轉換)可以將Char轉成數值。
14.2 System.String型別
String型別
String派生自Object,因此String物件(他的字元陣列)總是存在於堆上。
構造字串
構造String物件
C#不允許使用new操作符從字面值字串構造String物件
//不允許 String s=new String("test"); //允許 String s="test";
構造物件新例項的IL指令使newobj,但構造String物件使用的是Idstr,證明CLR實際是用一種特殊方式構造字面值String物件。
如果使用unsafe程式碼,可以從一個Char*或Sbyte*構造一個String,這時要使用new操作符,並呼叫由String型別提供的構造器。
操作字串
定義包含回車和換行符的字串的正確方法
String s="Hi"+Environment.NewLine+"there";
可以使用+操作符將幾個字串連線在一起
String s="Hi"+" "+"there";
上述程式碼中,所有字串都是字面值,所以C#能在編譯時連線他們,最終只將一個字串放到模組的元資料中。
對非字面值字串使用+操作符,連線則在執行時進行,執行時連線不要使用+操作符,這樣會在堆上建立多個字串物件,影響效能,建議使用StringBuilder
逐字字串
通常用於指定檔案或目錄的路徑,或者與正則表示式配合使用。
//指定應用程式路徑String file="C:\\Windows\\System32\\Notepad.exe"; //使用逐字字串 String file=@"C:\Windows\System32\Notepad.exe";
二者元資料沒有任何區別,但後者可讀性更好
字串是不可變的
String物件不可變,字串一經建立便不能更改、變長變短或者修改其中的任何字元。
它允許在一個字串上執行各種操作而不實際地更改字串,字串不可變還意味著在操縱或訪問字串時不會發生執行緒同步問題。
字串留用
CLR可通過一個String物件共享多個完全一致的String內容。
比較字串
相等性或排序
Equals
Compare
StartsWith
EndsWith
字串留用
CLR初始化時建立一個內部雜湊表
表中key是字串,值是對託管堆中的String物件的引用。
//使用這兩個方法可以訪問內部的雜湊表 public static String Intern(String str); public static String IsInterned(String str);
Intern方法
第一個方法Intern獲取一個String獲取它的雜湊碼,並在內部雜湊表中檢查是否有相匹配的,如果有完全相同的就返回對現有String的引用,不存在就建立字串的副本。返回對該副本的引用。
GC不能釋放內部雜湊表引用的字串,除非解除安裝或程序終止。
IsInterned方法
IsInterned方法也獲取一個String,在內部雜湊表中查詢,有匹配的就返回對這個歌留用字串物件的引用,沒有就返回null,不新增到雜湊表。
使用字串留用提升效能
除非顯式呼叫Intern方法,否則永遠不要以字串已留用為前提寫程式碼
使用字串留用可以用來提升效能
private static int NumTimesWordAppearsIntern(String word,String[] wordlist){ //假定wordlist中所有陣列元素都引用已留用的字串 word=String.Intern(word); int count=0; for(int wordnum=0;wordnum<wordlist.Length;wordnum++){ if(Object.ReferenceEquals(word,wordlist[wordnum])) count++; } return count; }
wordlist會包含對堆中同一個String物件的多個引用,比較指標就能知道單詞是否在數組裡。
字串池
編譯原始碼時,編譯器必須處理每個字面值字串,並在託管模組的元資料中嵌入。
同一個字串在原始碼中多次出現把他們嵌入元資料會使生成的檔案無謂地增大。
編譯器只在模組的元資料中只把字面值字串寫入一次。引用該字串的所有程式碼都被修改成引用元資料的同一個字串。
編譯器將單個字串的多個例項合併成一個例項顯著減少模組的大小。
14.3 高效率構造字串
StringBuilder型別
FCL提供了System.Text.StringBuilder型別對字串和字元進行高效動態處理,並返回處理好的String物件。
可以將StringBuilder想象為建立String物件地特殊構造器,方法獲取地應該是String而不是StringBuilder。
StringBuilder的成員特性
StringBuilder物件包含一個欄位,該欄位引用了Char結構構成的陣列。
利用StringBuilder的各個成員來操縱該字元陣列,高效率縮短字串或更改字串中的字元。
字串變大超過了實現分配的字元陣列大小,StringBuilder會自動分配新的、更大的陣列,複製字元並開始使用新陣列,前一個數組被GC。
StringBuilder轉換成String
用StringBuilder物件構造好字串後,呼叫StringBuilder的ToString即可將StringBuilder字元陣列轉換成String,這樣會在堆上新建String物件,其中包含呼叫ToString時存在於StringBuilder的字串,之後可以繼續處理StringBuilder中的字串,以後可以再次呼叫ToString把它轉換成另一個String物件。
構造StringBuilder物件
和String不同,StringBuilder對CLR來說沒什麼特別。
StringBuilder不是基元型別,要這樣構造StringBuilder物件:
StringBuilder sb=new StringBuilder();
StringBuilder構造器引數
StringBuilder提供了許多構造器,其中引數有:
①最大容量,指定了能放到字串中的最大字元數預設是Int32.MaxValue,約20億。一般不用更改,構造好了後就不能再變了。
②容量,Int32值,指定了StringBuilder維護的字元陣列的長度1,預設16.追加字元時StringBuilder會檢測陣列會不會超過設定的容量,超過的話會自動倍增容量。
③字元陣列,由Char結構構成的陣列,負責維護字串的字元內容。
StringBuilde在堆上分配新物件的情況
StringBuilder大多數成員都能更改字元陣列的內容,不會造成在託管堆上分配新物件。
StringBuilder只有以下兩種情況才會分配新物件動態構造字串,長度超過了設定的容量
①呼叫StringBuilder的ToString方法
②StringBuilder和String提供的方法不完全對應。需要經常相互轉換來使用需要的方法