1、字符
在。NET Framewole中,字符老是表示成16位Unicode代碼值,這簡化了國際化應用程序的開發。
每一個字符都表示成System.Char結構(一個值類型) 的一個實例。System.Char類型提供了兩個公共只讀常量字段:MinValue(定義成"\0")和MaxValue(定義成'\uffff')。
針對Char的一個實例,能夠調用GetUnicodeCategory方法,這個方法返回的是System.Globalization.UnicodeCategory枚舉類型的一個值。這個值支出該字符是控制字符、貨幣符號、小寫符號、大寫符號、標點符號、數學符號,仍是其餘字符(Unicode定義的字符)。
爲了簡化開發,Char類型還提供了幾個靜態方法,好比IsDigit,IsLotter,IsUpper,IsLower,isNumber等。其中大多數方法都在內部調用了GetUnicodeCategory,並簡單的返回true和false.
另外可調用ToLowerInvariant和ToUpperInvariant,以忽略語言文化(culture)的方式,將一個字符轉換成小寫和大寫形式。做爲另外一種替代方案,ToLower和ToUpper方法將字符轉換成小寫和大寫形式,可是轉換時要使用與調用線程關聯的靜態CurrenCulture屬性來得到。ToLower和ToUpper之因此須要語言文化信息,是由於字母大小寫的轉換是依賴於語言文化的。不一樣的語言文化,大小寫的形式也不盡相同。
可使用三種技術實現各類數值類型與Char實例的相互轉換:
1) 轉型(強制類型轉換) 要將一個Char轉換成爲一個數值(好比Int32),最簡單的方法就是轉型。在三種技術中,效率也是最高的,由於編譯器會生成中間語言(IL)指令來執行轉型,沒必要調用任何方法。
2) 使用Convert類型 System.Convert類型提供了幾個靜態方法來實現Char和數值類型的相互轉型。因此這些方法都是以checked方式進行轉換,因此一旦發現轉型會形成數據丟失,就會拋出OverflowException異常。
3) 使用IConvertible接口 Char類型和FCL的全部數值類型都實現的ICOnvertible接口。該接口定義了像ToUInt16和ToChar這些的方法。這種技術效率最差,由於在值類型上調用一個接口方法,要求對實例進行裝箱-- Char和全部數值類型都是值類型。若是某個類型不能轉換,或者轉換形成數據的丟失,IConvertible的方法會拋出System.InvalidCastException異常。
下面演示這三種方法的調用:
internal static class CharConvert {
public static void Go() {
Char c;
Int32 n;
// 使用C#轉型技術實現,強制類型轉換
c = (Char)65;
Console.WriteLine(c); // 顯示 "A"
n = (Int32)c;
Console.WriteLine(n); // 顯示 "65"
c = unchecked((Char)(65536 + 65));
Console.WriteLine(c); // 顯示 "A"
// 使用Convert進行轉換
c = Convert.ToChar(65);
Console.WriteLine(c); // 顯示 "A"
n = Convert.ToInt32(c);
Console.WriteLine(n); // Displays "65"
// 顯示Convert的範圍檢查
try {
c = Convert.ToChar(70000); // 對 16-bits 來講過大
Console.WriteLine(c); // 不知心
}
catch (OverflowException) {
Console.WriteLine("Can't convert 70000 to a Char.");
}
// 使用IConvertible進行轉換
c = ((IConvertible)65)。ToChar(null);
Console.WriteLine(c); // 顯示 "A"
n = ((IConvertible)c)。ToInt32(null);
Console.WriteLine(n); // 顯示 "65"
}
}
2、字符串
一個String表明一個不可變(immutable)的順序字符集。String直接派生自Object,因此它是一個引用類型。所以,String(字符串數組)老是存在於堆上,不會跑到棧上去。
String類型還實現了幾個接口,IComparable、ICloneable等。
1.構造字符串
許多編程語言(包括C#)都將String視爲一個基元類型--也就是說,編譯器容許在源代碼中直接表示文本常量字符型。編譯器將這些文本常量字符串放到模塊的元數據中,並在運行時加載和引用它們。
在C#中,不能使用new操做符從一個文本常量字符串構造一個String對象,相反,必須使用簡化的語法表示:
class Program
{
private static void Main(string[] args)
{
String s = "Hi";
Console.WriteLine(s);
}
}
編譯上述代碼,並檢查它的IL,會看到一下內容:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] string str)
L_0000: nop
L_0001: ldstr "Hi"
L_0006: stloc.0
L_0007: ldloc.0
L_0008: call void [mscorlib]System.Console::WriteLine(string)
L_000d: nop
L_000e: ret
}
IL指令的newobj用於構造一個對象的新實例。然而,上述IL代碼並無出現newobj之類,只有一個特殊的ldstr(即load string)指令,它用從元數據得到的一個文本常量字符串構造一個String對象。這證實CLR事實是用一種特殊的方式構造文本常量String對象。
C#提供了一些特殊的語法來幫助開發人員在源代碼中輸入文本常量字符串。對於換行符、回車符和退格這樣的特殊字符,C#採用了C/C++的轉義機制:
//包含回車符和換行符的字符串
String s ="Hi\r\nthere"
可是,通常不建議這麼作。由於在不一樣的平臺解釋是不一樣的,推薦使用System.Environment中定義的NewLine屬性。NewLine屬性是依賴於平臺的,他會一句底層平臺返回恰當的字符串。
若要在運行時將幾個字符串鏈接到一塊兒,請避免使用+操做符,由於它會在堆上建立多個字符串對象,而對象是須要垃圾回收的,從而影響性能。相反,應儘可能使用System.Text.StringBuilder類型。
最後,C#還提供了一種特殊的字符串聲明方式(@"xxx")。採用這種方式,引號之間的全部字符都會被視爲字符串的一部分。這種特殊聲明稱爲"逐字字符串",一般用於指定文件或目錄的路徑,或者配合正則表達式使用。
2.字符串是不可變的
String對象最重要的一個事實就是,它是不可變的(immytable)。也就是說,字符串一經建立就不能更改,不能變長、變短或修改其中任何字符。
字符串不可變也下面幾點好處:
1.它容許在字符串上執行任何操做,而不實際的更改字符串。
2.在操做或訪問字符串時不會發生線程同步問題。
3.CLR可經過一個String對象共享多個徹底一致的String內容。這樣能減小系統中的字符串屬性,從而節省內存,這就是"字符串留用"技術的目的。
考慮到性能方面的緣由,String類型和CLR是緊密集成的。具體的說,CLR知道String類型中定義的字段是如何佈局的,並且CLR會直接訪問這些字段。可是,爲了得到這種性能和直接訪問的好處,開發時只好將String定義爲密封類。
3.比較字符串
判斷字符串相等性或對字符串進行排序時,強烈建議調用下面列出的方法之一:
bool Equals (string value, StringComparison comparisonType)
static bool Equals (string a, string b, StringComparison comparisonType)
static int Compare (string strA, string strB, StringComparison comparisonType)
static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
進行排序時應該老是執行區分大小寫的比較。緣由是假如只是大小寫不一樣的兩個字符串被視爲相等,那麼每次對它們進行派時許,它們均可能按照不一樣的順序排列,從而形成用戶的迷惑。
上述代碼中的comparisonType參數要求獲取由System.StringComparison枚舉類型定義的某個值。這個枚舉類型是這樣定義的:
public enum StringComparison {
//使用區域敏感排序規則和當前區域比較字符串。
CurrentCulture,
//使用區域敏感排序規則、當前區域來比較字符串,同時忽略被比較字符串的大小寫。
CurrentCultureIgnoreCase,
//使用區域敏感排序規則和固定區域比較字符串。
InvariantCulture,
//使用區域敏感排序規則、固定區域來比較字符串,同時忽略被比較字符串的大小寫。
InvariantCultureIgnoreCase,
//使用序號排序規則比較字符串。
Ordinal,
//使用序號排序規則並忽略被比較字符串的大小寫,對字符串進行比較。
OrdinalIgnoreCase
}
另外,前面有兩個方法要求傳遞一個CompareOptions參數。這個參數要獲取有CompareOptions枚舉類型定義的一個值:
public enum CompareOptions {
None = 0,
//指示字符串比較必須忽略大小寫。
IgnoreCase = 1,
//指示字符串比較必須忽略不佔空間的組合字符,好比音調符號。
IgnoreNonSpace = 2,
//指示字符串比較必須忽略符號,如空白字符、標點符號、貨幣符號、百分號、數學符號、"&"符等等
IgnoreSymbols = 4,
//指示字符串比較必須忽略 Kana 類型
IgnoreKanaType = 8,
//指示字符串比較必須忽略字符寬度
IgnoreWidth = 16,
//指示字符串比較必須使用字符串排序算法。
StringSort = 0x20000000,
//指示必須使用字符串的連續 Unicode UTF-16 編碼值進行字符串比較(使用代碼單元進行代碼單元比較),這樣能夠提升比較速度,但不能區分區域性
Ordinal = 0x40000000,
//字符串比較必須忽略大小寫,而後執行序號比較。
OrdinalIgnoreCase = 0x10000000
}
接受一個CompareOptions實參的方法要求你必須顯式傳遞一個語言文化。若是傳遞了Ordinal或OrdinalIgnoreCase 標誌,這些Comoare方法會忽略指定的語言文化。
許多程序都將字符串用於內部編程目的,好比路徑名、文件名、URL、註冊表項/值等等。這些字符串一般只在程序內部使用,不會向用戶顯示。出於編程目的而比較字符串時,應該老是使用StringComparison.Ordinal或者CompareOptions.OrdinalIgnoreCase.這是字符串比較時最快的一種方式,由於在執行比較時,不須要考慮語言文化信息。
另外一方面,若是想以一種語言文化正確的方式來比較字符串(一般顯示給用戶),應該使用StringComparison.CurrentCulture或者StringComparison.CurrentCultureIgnoreCase.
提示:StringComparison.InvariantCulture和StringComparison.InvariantCultureIgnoreCase平時最好不要用。雖然這兩個值能保證比較是語言文化的正確性,但用它們比較用於內部編程目的的字符串,花費的事件要比執行一次序號比較長的多。
提示:執行序號比較以前,若是(想更改字符串中的字符的大小寫,應該使用String的ToUpperInvariant和ToLowerInvariant方法。對字符串進行正規化時,強烈建議使用ToUpperInvariant方法,而不要使用ToLowerInvariant方法,應爲Microsoft對執行大寫比較的代碼進行了優化。事實上,執行不須要區分大小寫的比較以前,FCL會自動將字符串正規化爲大寫形式。之因此不用ToUpper和ToLower方法,是由於它們對語言文化敏感。
4.字符串留用
檢查字符串的相等性是許多應用程序的常見操做--這個任務可能驗證損害性能。執行序號(ordinal)相等性檢查時,CLR快速測試兩個字符串是否包含相同數量的字符。若是是否認,字符串確定不相等;若是確定,字符串可能相等。而後,CLR必須比較每一個單獨的字符才能肯定。值得注意的是,在執行須要注意語言文化的比較是,CLR始終都要比較全部單獨的字符,由於兩個字符串即便長度不一樣,也多是相等的。
除此以外,若是在內存中複製同一個字符串的多個實例,會形成內存的浪費,由於字符串是"不可變"的。若是隻在內存中保留字符串的一個實例,那麼將顯着提升內存的利用率。須要引用字符串的全部變量只需指向單獨一個字符串對象。
若是引用程序常常對字符串進行區分大小寫、序號式比較,或者事先知道許多字符串對象都有相同的值,就能夠利用CLR的"字符串留用"機制來顯着提升性能。CLR初始化時會建立一個內部哈希表。在i這個表中,鍵(key)是字符串,而值(value)是對託管堆中String對象的引用。哈希表最開始是空的,String類提供了兩個方法,便於你訪問這個內部哈希表:
//檢索系統對指定 System.String 的引用
public static string Intern(string str)
//檢索對指定 System.String 的引用
public static string IsInterned(string str)
第一個方法Intern獲取一個String,得到它的哈希碼,並在內部哈希表中檢查是否有匹配的。若是存在一個徹底相同的字符串,就返回對這個字符串已經存在的String對象的一個引用。若是不存在,就建立字符串的副本,將副本添加到內部哈希表中,並返回對這個副本的一個引用 www.yztrans.com
和Intern方法同樣,IsInterned方法也獲取一個String,並在內部哈希表中查找它。若是哈希表中有一個匹配的字符串,IIsInterned就返回對這個留用的字符串對象的一個引用。然而,若是哈希表中沒有一個相匹配的字符串,IsInterned會返回null;它不會將字符串添加到哈希表中。
一個程序集加載時,CLR默認會留用程序集的元數據中描述的全部文本常量(literal)字符串。Microsoft知道可能由於額外的哈希表查找會形成性能顯着降低,因此如今是能夠禁用這個"特性"的。
根據ECMA規範,CLR可能選擇不留用那個程序集的元數據中定義的全部字符串。即便指定了CLR不留用那個程序集中的字符串,可是CLR也可能選擇對字符串進行留用,但不該該依賴於CLR的這種"自主"行爲。事實上,除非本身顯式調用String的Intern方法,不然永遠都不要以"字符串已留用"爲前提來寫本身的代碼。如下代碼演示了字符串留用:
public static void Go() {
String s1 = "Hello";
String s2 = "Hello";
Console.WriteLine(Object.ReferenceEquals(s1, s2));// 'False'
s1 = String.Intern(s1);
s2 = String.Intern(s2);
Console.WriteLine(Object.ReferenceEquals(s1, s2));// 'True'
}
在對ReferenceEquals方法的第一個調用中,s1s2中的"Hello"字符串對象的引用是不一樣的,因此應該顯示False.然而,若是在CLR的4.0版本上運行,會發現顯示True.由於這個版本的CLR選擇了忽略C#編譯器中字符串不留用的設置。
5. 字符串池
編譯源代碼時,編譯器必須處理每一個文本常量字符串,並在託管模塊的元數據上嵌入字符串。若是同一個文本常量字符串在源代碼中屢次出現,將全部這些字符串都嵌入元數據中,會陡然增大最終生成的文件。
爲了解決這個問題,許多編譯器(包括C#編譯器)都只在模塊的元數據中將文本常量字符串寫入一次。引用該字符串的全部代碼都會被修改,以引用元數據中的同一個字符串。編譯器這種將單個字符串的多個實例合併爲一個實例的作法,能夠顯着減小模塊大小。
3、高效率構造字符串
因爲String類型是一個不可變的字符串,因此FCL提供了另外一個名爲System.Text.StringBuilder的類型,可利用它高效率得對字符串和字符進行動態代理,最後基於處理結果建立一個String.
從邏輯上說,StringBuilder對象包含一個字段,該字段引用了有Char結構構成的一個數組。可利用StringBuilder的各個成員來操做這個字符數組,高效率的縮短或更改字符串中的字符。若是字符串變大,超過已分配的字符數組的大小,StringBuilder會自動分配一個新的、更大的數組,複製字符,並開始使用新數組。前一個數組會被垃圾回收 www.jx-jf.com
4、獲取對象的字符串表示:ToString
在。NET Framework中能夠調用ToString方法來和獲取任何對象的字符串表示。System.Objetc定義了一個public、virtual、無參的ToString方法,因此在任何類型的一個實例上都能調用該方法。在語義上,ToString返回表明對象當前值的一個字符串,並且這個字符串應該根據調用線程當前的語言文化進行格式化。
5、解析字符串來獲取對象:Parse
能解析一個字符串的任何類型都提供了一個名爲Parse的public static方法。該方法獲取一個String對象,並返回類型的一個實例。從某種意義上說,Parse扮演了一個工廠的角色。在FCL中,全部數值類型,DateTime、TimeSpan以及一些其餘類型均提供了Parse方法 www.yzyedu.com
git