基本類型
字符、字符串和文本處理
字符
基本概念
-
基本結(jié)構(gòu)
- .Net Framework中,字符總是表示成16位的Unicode代碼值。
- 每個(gè)字符都是System.Char的實(shí)例,注意,該類型為值類型。
- Char.MaxValue='/uffff',表示65535。
Culture(字符或者字符串有文化的差異,需要注意)
-
轉(zhuǎn)換(效率由高到低)
-
轉(zhuǎn)型(強(qiáng)制類型轉(zhuǎn)換)
Char a = (Int32)65;// A -
使用Convert類型
Char a = Convert.ToChar(65); // A -
使用IConvertible接口
Char a = (IConvertible(65)).ToChar(null); // A
-
-
思考這兩種寫法有什么不同?
// 思考:這兩種寫法有什么不同? char b = unchecked((Char)(65536*2 + 65)); Console.WriteLine(b); Char d = unchecked(Convert.ToChar(70000));// 這種是編譯不過的 Console.WriteLine(d);-
IL分析
// 有效的IL代碼 .entrypoint // 代碼大小 30 (0x1e) .maxstack 1 .locals init (char V_0, char V_1) IL_0000: nop IL_0001: ldc.i4.s 65 // 實(shí)際強(qiáng)制轉(zhuǎn)換會減去65536的整數(shù)倍后再進(jìn)行轉(zhuǎn)換。 IL_0003: stloc.0 IL_0004: ldloc.0 IL_0005: call void [mscorlib]System.Console::WriteLine(char) IL_000a: nop IL_000b: ldc.i4 0x11170 // 已經(jīng)超過Char.MaxValue IL_0010: call char [mscorlib]System.Convert::ToChar(int32) IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: call void [mscorlib]System.Console::WriteLine(char) IL_001c: nop IL_001d: ret
-
參考內(nèi)容
字符串
String
基本概念
-
基本結(jié)構(gòu)
- string表示一個(gè)不可變的順序字符集(immutable)。
- String對象(它的字符數(shù)組)總是存在于堆上。
- 特別說明一點(diǎn),struct上面的string也不例外,若是局部變量,在Stack上,若是引用類型的成員,則在Heap上。但是注意的是,當(dāng)struct拷貝一個(gè)副本的時(shí)候,引用的堆控件也會重新分配初始化。
- 關(guān)于struct的說明,可以參考MSDN-Struct
- string不允許使用new關(guān)鍵字構(gòu)造string對象。
- 字符串換行建議使用Environment.NewLine,而不是/r/n。
- 編程技巧:要在序號比較前更改字符串中的字符的大小寫,應(yīng)該使用String.ToUpperInvariant進(jìn)行正規(guī)化(normalizing),微軟對執(zhí)行大寫比較代碼進(jìn)行過優(yōu)化。
-
語言文化(Culture)
- 后續(xù)補(bǔ)上,目前用到不多
-
字符串留用(string interning)
-
原理
1. 原理說明 CLR初始化的時(shí)候創(chuàng)建一個(gè)內(nèi)部哈希表,在這個(gè)表中,鍵(key)是字符串,而值(value)是對托管堆中的string對象的引用,開始時(shí)候,哈希表為空。 暴露的API: - public static String Intern(String str); - public static String IsInterned(String str); 2. 影響CLR的特性 - CompilationRelaxationAttribute
-
字符串池
高效處理字符串
- StringBuilder
- ToString
- IFormatProvider
- Parse
安全字符串
- 后續(xù)整理
文本處理
編碼(后續(xù)整理)
UTF-16(Unicode編碼)
-
UTF-8
// 簡單案例 public class EncodingTest { public static void Run() { string str = "hello,world"; // 獲取utf8 var utf8Encoding = Encoding.UTF8; // 將字符串編譯成字符串?dāng)?shù)組 var encodingBytes = utf8Encoding.GetBytes(str); // 顯示編碼好的值 Console.WriteLine(BitConverter.ToString(encodingBytes)); // 解碼 var str2 = utf8Encoding.GetString(encodingBytes); Console.WriteLine(str2); Console.WriteLine("是否相等:" + Object.ReferenceEquals(str, str2)); } }
枚舉類型和位標(biāo)志
基本概念
枚舉是值類型
-
使用枚舉的理由
- 枚舉類型使程序更容易編寫、閱讀和維護(hù)。
- 枚舉是強(qiáng)類型的,不容易寫錯(cuò)。
-
常見的用法
public class EnumTest { public static void Run() { // 強(qiáng)制轉(zhuǎn)換 ColorEnum color = (ColorEnum)1; // Pink if(color == ColorEnum.Pink) { Console.WriteLine("強(qiáng)制轉(zhuǎn)換成功"); } // 數(shù)字轉(zhuǎn)換 int num = (int)color; // 1 Console.WriteLine("轉(zhuǎn)換成數(shù)字:" + num); // 獲取枚舉項(xiàng)的名稱 string name = Enum.GetName(typeof(ColorEnum), 1); // Pink Console.WriteLine("查找到的枚舉值為1的名稱:" + name); } } public enum ColorEnum { Orange, Pink }
位標(biāo)志
-
普通枚舉和位標(biāo)志的區(qū)別
- 位標(biāo)志為可以用來表示一組可以組合的枚舉類型。
- 位標(biāo)志表示位集合。
- 枚舉值可以從0開始,按2的n-1次方表示,但是不一定是2的n次方。
- 可以用[Flags]特性標(biāo)注位標(biāo)志。
-
案例分析
public class Test { public static void Main(string[] args) { // 位標(biāo)志 MyFlagEnum actions = MyFlagEnum.Close | MyFlagEnum.Open;// 增加,等于十進(jìn)制的3,并裝箱到actions指向的Heap中MyFlagEnum Console.WriteLine(actions.ToString("F")); // Close,Open MyFlagEnum myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "open", true); Console.WriteLine(myFlag.ToString("F"));// Open myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "2", false); Console.WriteLine(myFlag.ToString("F"));// Close myFlag = (MyFlagEnum)Enum.Parse(typeof(MyFlagEnum), "3", false); Console.WriteLine(myFlag.ToString("F")); // Open,Close myFlag = MyFlagEnum.All | MyFlagEnum.None; // 為什么這里只會出現(xiàn)ALL,從最大值計(jì)算減掉當(dāng)前值,向下找組合,但是0不會給找到。 Console.WriteLine(myFlag.ToString("F"));// ALL } } [Flags] public enum MyFlagEnum { None = 0, Open = 0x0001, Close = 0x0002, All = 0x001F //十進(jìn)制31 }-
IL代碼分析
.class public auto ansi beforefieldinit ILLearning.Test extends [mscorlib]System.Object { .method public hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 198 (0xc6) .maxstack 3 .locals init (valuetype ILLearning.MyFlagEnum V_0, valuetype ILLearning.MyFlagEnum V_1) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: box ILLearning.MyFlagEnum IL_0009: ldstr "F" IL_000e: call instance string [mscorlib]System.Enum::ToString(string) IL_0013: call void [mscorlib]System.Console::WriteLine(string) IL_0018: nop IL_0019: ldtoken ILLearning.MyFlagEnum IL_001e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0023: ldstr "open" IL_0028: ldc.i4.1 // true,在IL中表示1,false表示0 IL_0029: call object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type, string, bool) IL_002e: unbox.any ILLearning.MyFlagEnum IL_0033: stloc.1 IL_0034: ldloc.1 IL_0035: box ILLearning.MyFlagEnum IL_003a: ldstr "F" IL_003f: call instance string [mscorlib]System.Enum::ToString(string) IL_0044: call void [mscorlib]System.Console::WriteLine(string) IL_0049: nop IL_004a: ldtoken ILLearning.MyFlagEnum IL_004f: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0054: ldstr "2" IL_0059: ldc.i4.0 IL_005a: call object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type, string, bool) IL_005f: unbox.any ILLearning.MyFlagEnum IL_0064: stloc.1 IL_0065: ldloc.1 IL_0066: box ILLearning.MyFlagEnum IL_006b: ldstr "F" IL_0070: call instance string [mscorlib]System.Enum::ToString(string) IL_0075: call void [mscorlib]System.Console::WriteLine(string) IL_007a: nop IL_007b: ldtoken ILLearning.MyFlagEnum IL_0080: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0085: ldstr "3" IL_008a: ldc.i4.0 IL_008b: call object [mscorlib]System.Enum::Parse(class [mscorlib]System.Type, string, bool) IL_0090: unbox.any ILLearning.MyFlagEnum IL_0095: stloc.1 IL_0096: ldloc.1 IL_0097: box ILLearning.MyFlagEnum IL_009c: ldstr "F" IL_00a1: call instance string [mscorlib]System.Enum::ToString(string) IL_00a6: call void [mscorlib]System.Console::WriteLine(string) IL_00ab: nop IL_00ac: ldc.i4.s 31 IL_00ae: stloc.1 IL_00af: ldloc.1 IL_00b0: box ILLearning.MyFlagEnum IL_00b5: ldstr "F" IL_00ba: call instance string [mscorlib]System.Enum::ToString(string) IL_00bf: call void [mscorlib]System.Console::WriteLine(string) IL_00c4: nop IL_00c5: ret } // end of method Test::Main .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // 代碼大小 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } // end of method Test::.ctor } // end of class ILLearning.Test .class public auto ansi sealed ILLearning.MyFlagEnum extends [mscorlib]System.Enum { .custom instance void [mscorlib]System.FlagsAttribute::.ctor() = ( 01 00 00 00 ) .field public specialname rtspecialname int32 value__ .field public static literal valuetype ILLearning.MyFlagEnum None = int32(0x00000000) .field public static literal valuetype ILLearning.MyFlagEnum Open = int32(0x00000001) .field public static literal valuetype ILLearning.MyFlagEnum Close = int32(0x00000002) .field public static literal valuetype ILLearning.MyFlagEnum All = int32(0x0000001F) } // end of class ILLearning.MyFlagEnum
-
數(shù)組
基本概念
- 數(shù)組為引用類型,是在托管堆中分配的。
- 數(shù)組隱式繼承自System.Array
數(shù)組轉(zhuǎn)型
對于元素類型為引用類型的數(shù)組,CLR允許將數(shù)組元素從一種類型轉(zhuǎn)型為另一種。
前提是數(shù)組維數(shù)相同,而且必須存在從元素源類型到目標(biāo)類型的隱式轉(zhuǎn)換或者顯式轉(zhuǎn)換。
-
CLR不允許將值類型轉(zhuǎn)換成其他任何類型。
// 測試類型轉(zhuǎn)換 FileStream[,] fs = new FileStream[5, 10]; // 隱式轉(zhuǎn)換 object[,] obj = fs;// 支持向上轉(zhuǎn)型,協(xié)變性 // 轉(zhuǎn)型失敗一:維度不同,不允許轉(zhuǎn)型 // Stream[] fs2 = obj; // 顯示轉(zhuǎn)型,成功 Stream[,] fs2 = (Stream[,])obj;// 逆變性 // 轉(zhuǎn)型失敗二:編譯沒有問題,運(yùn)行有問題,類型不匹配 // string[,] arrStr = (string[,])obj; // 值類型測試 Int32[] arrInt = new int[10]; // 轉(zhuǎn)型失敗三:值類型不允許轉(zhuǎn)換成引用類型 // object[] arrObject = arrInt; // 轉(zhuǎn)型失敗四:值類型不允許轉(zhuǎn)換成其他值類型 //Double[] arrDouble = arrInt;
數(shù)組內(nèi)部工作原理
- 隱式派生自System.Array
- 所有數(shù)組隱式實(shí)現(xiàn)IEnumerable、ICollection和IList。
數(shù)組的內(nèi)部工作原理
-
CLR支持兩種不同的數(shù)組
- 下限為0的一維數(shù)組,也稱SZ(single-dimensional,zero-based)數(shù)組或者向量(Vector)。
- 下限未知的一維或多維數(shù)組。
-
演示二維數(shù)組的三種方式(安全、交錯(cuò)和不安全)
using System; namespace ILLearning { public class ArrayDemoTest { private static int c_numElements = 10000; public static void Main() { Int32[,] a2Dim = new Int32[c_numElements, c_numElements]; // 聲明交錯(cuò)數(shù)組(向量構(gòu)成的向量) int[][] aJagged = new Int32[c_numElements][]; for (int i = 0; i <c_numElements; i++) { aJagged[i] = new Int32[c_numElements]; } Safe2DimArrayAccess(a2Dim); SafeJaggedArrayAccess(aJagged); Unsafe2DimArrayAccess(a2Dim); } /// <summary> /// CLR安全方式訪問 /// </summary> /// <param name="a"></param> /// <returns></returns> private static Int32 Safe2DimArrayAccess(Int32[,] a) { Int32 sum = 0; for (int i = 0; i < c_numElements; i++) { for (int j = 0; j < c_numElements; j++) { sum += a[i, j]; } } return sum; } /// <summary> /// 交錯(cuò)訪問 /// </summary> /// <param name="a"></param> /// <returns></returns> private static Int32 SafeJaggedArrayAccess(Int32[][] a) { Int32 sum = 0; for (int i = 0; i < c_numElements; i++) { for (int j = 0; j < c_numElements; j++) { sum += a[i][j]; } } return sum; } private static unsafe Int32 Unsafe2DimArrayAccess(Int32[,] a) { Int32 sum = 0; // fixed的作用: /* fixed 語句可防止垃圾回收器重新定位可移動的變量。 fixed 語句僅允許存在于不安全的上下文中。 fixed 還可用于創(chuàng)建固定大小的緩沖區(qū)。 */ fixed (Int32* pi = a)// 聲明指針pi,指向CLR類型a { for (Int32 i = 0; i < c_numElements; i++) { Int32 baseofDim = i * c_numElements; for (int j = 0; j < c_numElements; j++) { sum += pi[baseofDim+j]; } } } return sum; } } }-
待分析unsafe代碼IL代碼
.method private hidebysig static int32 Unsafe2DimArrayAccess(int32[0...,0...] a) cil managed { // 代碼大小 118 (0x76) .maxstack 4 .locals init (int32 V_0, int32* V_1, int32[0...,0...] pinned V_2, int32 V_3, int32 V_4, int32 V_5, bool V_6, bool V_7, int32 V_8) IL_0000: nop IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: ldarg.0 IL_0004: dup IL_0005: stloc.2 IL_0006: brfalse.s IL_0010 IL_0008: ldloc.2 IL_0009: callvirt instance int32 [mscorlib]System.Array::get_Length() IL_000e: brtrue.s IL_0015 IL_0010: ldc.i4.0 IL_0011: conv.u IL_0012: stloc.1 IL_0013: br.s IL_001f IL_0015: ldloc.2 IL_0016: ldc.i4.0 IL_0017: ldc.i4.0 IL_0018: call instance int32& int32[0...,0...]::Address(int32, int32) IL_001d: conv.u IL_001e: stloc.1 IL_001f: nop IL_0020: ldc.i4.0 IL_0021: stloc.3 IL_0022: br.s IL_005d IL_0024: nop IL_0025: ldloc.3 IL_0026: ldsfld int32 ILLearning.ArrayDemoTest::c_numElements IL_002b: mul IL_002c: stloc.s V_4 IL_002e: ldc.i4.0 IL_002f: stloc.s V_5 IL_0031: br.s IL_0049 IL_0033: nop IL_0034: ldloc.0 IL_0035: ldloc.1 IL_0036: ldloc.s V_4 IL_0038: ldloc.s V_5 IL_003a: add IL_003b: conv.i IL_003c: ldc.i4.4 IL_003d: mul IL_003e: add IL_003f: ldind.i4 IL_0040: add IL_0041: stloc.0 IL_0042: nop IL_0043: ldloc.s V_5 IL_0045: ldc.i4.1 IL_0046: add IL_0047: stloc.s V_5 IL_0049: ldloc.s V_5 IL_004b: ldsfld int32 ILLearning.ArrayDemoTest::c_numElements IL_0050: clt IL_0052: stloc.s V_6 IL_0054: ldloc.s V_6 IL_0056: brtrue.s IL_0033 IL_0058: nop IL_0059: ldloc.3 IL_005a: ldc.i4.1 IL_005b: add IL_005c: stloc.3 IL_005d: ldloc.3 IL_005e: ldsfld int32 ILLearning.ArrayDemoTest::c_numElements IL_0063: clt IL_0065: stloc.s V_7 IL_0067: ldloc.s V_7 IL_0069: brtrue.s IL_0024 IL_006b: nop IL_006c: ldnull IL_006d: stloc.2 IL_006e: ldloc.0 IL_006f: stloc.s V_8 IL_0071: br.s IL_0073 IL_0073: ldloc.s V_8 IL_0075: ret } // end of method ArrayDemoTest::Unsafe2DimArrayAccess
-
不安全的數(shù)組(后續(xù)補(bǔ)充)
其他主題
數(shù)組的傳遞和返回
數(shù)組作為實(shí)參傳遞給方法時(shí)候,實(shí)際傳遞的是數(shù)組的引用。
-
Array.Copy方法執(zhí)行的是淺拷貝,淺拷貝怎么理解?
public class Test { public static void Main(string[] args) { // Array.Copy string[] hhs = new string[3]; hhs[0] = "hi"; hhs[1] = ","; string[] hhs2 = new string[5]; hhs.CopyTo(hhs2,0);// 實(shí)際上,這里的效果是深復(fù)制,應(yīng)該是兩次New都有在堆中分配了內(nèi)存空間,不會再單獨(dú)開辟棧引用,指向其他的內(nèi)存空間 } }-
主要IL代碼分析
.module test.exe // MVID: {B90E3704-F778-4BDE-8F93-5D1D1296068D} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x06870000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit ILLearning.Test extends [mscorlib]System.Object { .method public hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 41 (0x29) .maxstack 3 .locals init (string[] V_0, string[] V_1) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: newarr [mscorlib]System.String // 創(chuàng)建數(shù)組hhs IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.0 IL_000a: ldstr "hi" IL_000f: stelem.ref // 用計(jì)算堆棧上的對象 ref 值(O 類型)替換給定索引處的數(shù)組元素。 IL_0010: ldloc.0 IL_0011: ldc.i4.1 IL_0012: ldstr "," IL_0017: stelem.ref IL_0018: ldc.i4.5 IL_0019: newarr [mscorlib]System.String IL_001e: stloc.1 IL_001f: ldloc.0 IL_0020: ldloc.1 IL_0021: ldc.i4.0 IL_0022: callvirt instance void [mscorlib]System.Array::CopyTo(class [mscorlib]System.Array, int32) IL_0027: nop IL_0028: ret } // end of method Test::Main
-
-
數(shù)組的實(shí)參傳遞
// 測試數(shù)組傳遞的是引用 public static void Test() { string[] hhs = new string[3]; hhs[0] = "hi"; hhs[1] = ","; foreach (var item in hhs) { Console.WriteLine(item);// hhs[0] = hi } SayHi(hhs); foreach (var item in hhs) { Console.WriteLine(item);// hhs[0] = "HelloWorld" } } public static void SayHi(string[] hehes) { if (hehes != null && hehes.Count() > 1) { hehes[0] = "HelloWorld"; } }
創(chuàng)建下限非零的數(shù)組
-
演示案例
// 目前比較少用 public sealed class DynamicArrays { public static void Run() { Int32[] lowerBounds = { 2005, 1 }; Int32[] lengths = { 5, 4 }; Decimal[,] quarterlyRevernue = (Decimal[,])Array.CreateInstance(typeof(Decimal), lengths, lowerBounds); Console.WriteLine("{0,4} {1,9} {2,9} {3,9} {4,9}", "Year","Q1", "Q2", "Q3", "Q4"); Int32 firstYear = quarterlyRevernue.GetLowerBound(0); Int32 lastYear = quarterlyRevernue.GetUpperBound(0); int firstQuarter = quarterlyRevernue.GetLowerBound(1); int lastQuarter = quarterlyRevernue.GetUpperBound(1); for(Int32 year = firstYear;year <= lastYear; year++) { Console.Write(year + " "); for (int quarter = firstQuarter; quarter <= lastQuarter; quarter++) { Console.Write("{0,9:C}", quarterlyRevernue[year, quarter]); } Console.WriteLine(); } } }
可空值類型
基本概念
- System.Nullable<T>仍然為值類型
- ??可空符號優(yōu)化