C#中的數組
- 數組概念
- 數組是對象
- 數組的聲明
- 數組的實例化
- 顯式的初始化 數組
- 數組快捷語法
- 隱式的初始化 數組
- 知識點串起來組成例子
數組 是 由一個 變量名 表示的 同一組同類型 的 數據元素。
數組 一旦創建,大小就固定了;
C# 不像 javascript 一樣,C#是不支持動態數組的。
數組從 0 開始,范圍:0~n-1 (n是數量)
有兩種類型的數組:一維數組 和 多維數組
數組實例是從 System.Array類 繼承類型的對象。
數組是對象
數組是 引用類型;
數組的元素 的類型 既可以是引用類型也可以是值類型;分別稱為:引用類型數組 和 值類型數組。
數組的聲明
long[] secondArray; int[,,] b; // 三維數組,如果沒有逗號就是一維數組,一個逗號就是二維數組 錯誤的寫法: long b[]; // 錯誤,中括號在變量名后面,這是 C/C++的寫法,C#不適用
數組的實例化
// 一維數組,聲明 a 數組、創建 a 數組實例,沒有初始化 int[] a = new int[2]; Student[] std = new Student[]; // 三維數組 初始化 int[,,] arr3 = new int[3, 5, 2];
注意:與對象創建表達式不同,數組的初始化沒有 圓括號,即使是引用類型數組的初始化
默認值:
創建數組實例后,其每個元素都有默認值,string 的為空字符串,int 的為 0 ,bool 的為 false,引用類型的為 null;
數組的初始化,顯式的初始化 數組
// 不必輸入多少維,也就是 : = new int[1]{...} int[] intArr = new int[] {1, 2, 4, 11, 84}; int[,] intArr2 = new int[,] {{10,1}, {2, 10}, {11, 9}};
數組快捷語法
int[] arr1 = new int[3] {1, 33, 1}; // 等價於 int[] arr1 = {1, 33, 1}; int[,] arr2 = new int[,] {{10,1}, {2, 10}}; // 等價於 int[,] arr2 = {{10,1}, {2, 10}};
數組的初始化,隱式的初始化 數組
上面我們一直都在數組的聲明開始處顯式指定數組類型,但是,和其他局部變量一樣,數組可以是隱式類型的:
int[] a = new int[]{1, 2}; // var的隱式推斷,因為可以從 {1,2} 推斷出數組時int類型 var a = new []{1, 2}; int[,] b = new int[,] {{10,1}, {2, 10}}; // 隱式 var b = new [,] {{10,1}, {2, 10}}; string[] c = new string[] {"hello", ", world", "!"}; // 隱式 var c = new [] {"hello", ", world", "!"};
上面知識點綜合串起來組成一個例子
// 聲明、創建 和 初始化 一個隱式類型的數組 var arr = new int[,] {{0, 1, 2}, {10, 11, 12}}; for(int i = 0; i < 2; i++){ for(int j = 0; j < 3; j++){ Console.WriteLine($"[{i},{j} is {arr[i, j]}"); } }
接下來,是深入數組
- 交錯數組
- foreach 循環數組
- 迭代的變量是只讀的
- foreach語句 與 多維數組
- foreach語句 與 交錯數組
- 數組協變
- 數組的屬性和方法
- clone 克隆數組
- 比較各個數組的區別
- 數組 與 ref返回 和 ref局部變量
交錯數組
交錯數組,其實就是數組的數組。
與矩形數組不同,交錯數組的子數組的元素個數可以不同。
// 二維交錯數組 int[][] jagArr = new int[3][]; // 讀:jagArr是3個int數組的數組 jagArr[0] = new int[] {11, 34}; jagArr[1] = new int[] {41, 34, 99, 12, 7}; jagArr[2] = new int[] {11, 110, 781};
交錯數組不能在一個步驟中完成:即不能一個語句搞定 創建、聲明、實例化;
上述中,jagArr 第一個中括號為3,但是第二個中括號不能有數字;
下面是矩形數組 和 交錯數組 的 結構:
一維數組:有特定性能優化指令。矩形數組沒有,並且不在相同級別進行優化。
一維數組(可被優化)的交錯數組 比 矩形數組(不能被優化) 更高效;
矩形數組復雜度低,因為它被作為一個單元而不是數組的數組,交較之 交錯數組 復雜度高;
foreach 語句循環數組
foreach 語句除了可以循環數組,其實它還可以循環其他集合類型;
foreach 的迭代變量是 臨時 的,也是只讀的;
int[] arr1 = {10, 11, 12, 13}; foreach(int item in arr1){ Console.WriteLine($"Item Value: {item}"); // item 這個臨時的變量是只讀的,無法修改它 }
迭代變量是只讀的
因為foreach語句迭代的變量是只讀的,所以我們不能改變它;
但是,對於 值類型 和 引用類型的數組,使用foreach語句去 迭代 出來的臨時變量,是有區別的;
對於值類型的數組:
int[] arr = {1, 2, 3}; foreach(int itme in arr){ item++; // 編譯錯誤,不得改變變量值 }
對於引用類型的數組:
我們仍然不能改變迭代變量,但是迭代變量只是保存了數據的引用,而不是數據本身(如果不懂此處,請學習有關 引用類型 和 值類型 的具體內存指向),因此,雖然不能改變引用,但我們可以通過迭代變量改變數據:
class AClass{ public int MyField = 0; } AClass[] aclass = new AClass[4]; for(int i = 0; i < 4; i++){ aclass[i] = new AClass(); aclass[i].MyField= i; } foreach(AClass item in aclass){ item.MyField += 10; // 改變數據,是可以的 } foreach(AClass item in aclass){ Console.WriteLine($"{item.MyField}"); }
foreach語句 和 多維數組
在多維數組中,元素的處理次序是最右邊的索引號最先遞增。當索引從0到長度減1時,開始遞增它左邊的索引,右邊的索引被重置成0。(這句話有點拗口,看下面例子即可)
// 例子:矩形數組 int total = 0; int[,] arr = {{10, 11}, {12, 13}}; foreach(var element in arr){ total += element; Console.WriteLine($"Element: {element}, Current Total: {total}"); } // 輸出: Element: 10, Current Total: 10; Element: 11, Current Total: 21; Element: 12, Current Total: 33; Element: 13, Current Total: 46;
foreach語句 和 交錯數組
交錯數組是數組的數組,所以我們必須為交錯數組中的每一個維度使用獨立的foreach語句。
例:
int total = 0; int[][] arr = new int[2][]; arr[0] = new int[]{10, 11}; arr[1] = new int[]{12, 13, 14} foreach(int[] array in arr){ Console.WriteLine("開始新的數組"); foreach(int item in array){ total += item; Console.WriteLine($" Item: {item}, Current Total: {total}"); } } // 輸出: 開始新的數組 Item: 10, Current Total: 10 Item: 11, Current Total: 21 開始新的數組 Item: 12, Current Total: 33 Item: 13, Current Total: 46 Item: 14, Current Total: 60
數組協變
在某些情況下,即使某個對象不是數組的基類型,也可以把它賦值給數組元素。這種情況叫作 數組協變(array covariance)。在下面的情況下可以使用數組協變。
- 數組是引用類型的數組。
- 在賦值的對象類型和數組基類型之間有隱式轉換或顯式轉換。
例如,如下代碼聲明了兩個類,A和B,其中B類繼承自A類。最后一行展示了把類型B的對象賦值給類型4的數組元素而產生的協變:
class A {...} class B: A {...} A[] AArray1 = new A[3]; A[] AArray2 = new A[3]; // 普通:將A類型的對象賦值給A類型的數組 AArray1[0] = new A(); AArray1[1] = new A(); AArray[2] = new A(); // 協變:將B類型的對象賦值給A類型的數組 AArray2[0] = new B(); AArray2[1] = new B(); AArray2[2] = new B();
注意:值類型數組沒有協變!!
數組繼承來的有用成員
因為數組時繼承至 System.Array 類的,這就相當於數組繼承了里面的一些屬性和方法,包括:
例子:
public static void PrintArray(int[] a){ foreach(var x in a){ Console.WriteLine($"{x} "); } Console.WriteLine(""); } int[] arr = new int[] {15, 20, 5, 25, 10}; PrintArray(arr); Array.Sort(arr); PrintArray(arr); Array.Reverse(arr); PrintArray(arr); Console.WriteLine(); Console.WriteLine($"Rank = {arr.Rank}, Length = {arr.Length}"); Console.WriteLine($"GetLength(0) = {arr.GetLength(0)}"); Console.WriteLine($"GetType() = {arr.GetTeype()}"); // 輸出: 15 20 5 25 10 5 10 15 20 25 25 20 15 10 5 Rank = 1, Length = 5 GetLength(0) = 5 GetType() = System.Int32[]
Clone() 克隆數組
這是對數組的 淺復制,也就是說,它只創建了數組本身的克隆。
如果是引用類型數組,它不會復制元素引用的對象。
對於值類型數組和引用類型數組而言,這有不同的結果。
- 克隆 值類型 數組 會產生兩個獨立數組。
- 克隆 引用類型 數組 會產生指向相同對象的兩個數組。
Clone方法返回object類型的引用,它必須被強制轉換成數組類型。
值類型的例子分析:
int[] intArr1 = {1, 2, 3}; // 步驟 1 int[] intArr2 = (int[])intArr1.Clone(); // 步驟 2 intArr2[0] = 100; intArr2[1] = 200; intArr2[2] 300; // 步驟 3
引用類型的例子分析:
class A{ public int Value = 5; } A[] AArray1 = new A[3]{new A(), new A(), new A()}; // 步驟1 A[] AArray2 = (A[])AArray1.Clone(); // 步驟2 AArray2[0].Value = 100; AArray2[1].Value = 200; AArray2[2].Value = 300; // 步驟 3
比較各個數組的區別
數組 與 ref返回 和 ref局部變量
請先回顧下ref,不清楚這個ref關鍵字的請先復習下 ref 關鍵字的概念和用法;
利用ref返回功能,可以把一個引用作為返回值傳到方法體外,而利用ref局部變量,你可以在調用域內使用這個引用。例如,下面的代碼定義了一個叫作 PointerToHighestPositive 的方法。這個方法接受一個數組作為參數,並且返回對該數組元素的引用,而不是元素中的int 值。然后,在調用域,你可以通過ref局部變量給這個元素賦值。
public static ref int PointerToHighestPositive(int[] numbers) { int highest = 0; int indexOfHighest = 0; for (int i = 0; i < numbers.Length; i++) { if (numbers[i] > highest) { indexOfHighest = i; highest = numbers[indexOfHighest]; } } return ref numbers[indexOfHighest]; } static void Main() { int[] scores = { 5, 80 }; Console.WriteLine($"之前:{scores[0]}, {scores[1]}"); ref int locationOfHighest = ref PointerToHighestPositive(scores); locationOfHighest = 0; Console.WriteLine($"之后:{scores[0]}, {scores[1]}"); } // 輸出 之前:5, 80 之后:5, 0