C#圖解教程 第十二章 陣列
陣列
陣列
陣列實際上是由一個變數名稱表示的一組同類型的資料元素。每個元素通過變數名稱和一個或多個方括號中的索引來訪問:
陣列名 索引 ↓ ↓ MyArray[4]
定義
讓我們從C#中與陣列有關的重要定義開始
- 元素 陣列的獨立資料項稱為元素。陣列的所有元素必須是同類型的,或繼承自相同的型別
- 秩/維度 陣列可以有任何為正數的維度數。陣列的維度數稱作秩(rank)
- 維度長度 陣列的每個維度都有一個長度,就是這個方向的位置個數
- 陣列長度 陣列的所有維度中的元素的總和稱為陣列的長度
重要細節
關於C#陣列的一些要點:
- 陣列一旦建立,大小就固定了。C#不支援動態陣列
- 陣列索引號從0開始。如果維度長度是n,索引號範圍就是0到n-1
陣列的型別
C#提供兩種型別的陣列
- 一維陣列可以認為是單行元素或元素向量
- 多維陣列是由主向量中的位置組成的。每個位置本身又是一個數組,稱為子陣列(subarray)。子陣列向量中的位置本身又是一個子陣列
另外,有兩種型別的多維度陣列:矩形陣列(rectangular array)和交錯陣列(jagged array),它們有如下特性:
- 矩形陣列
- 某個維度的所有子陣列具有相同長度的多維陣列
- 不管有多少個維度,總是使用一組方括號
int x=myArray2[4,6,1]
- 交錯陣列
- 每個子陣列都是獨立陣列的多維度陣列
- 可以有不同長度的子陣列
- 為陣列的每個維度使用一對方括號
jagArray1[2][7][4]
陣列是物件
陣列例項是從System.Array繼承的物件。由於陣列從BCL(Base Class Library)基類繼承,它們也繼承了很多有用的方法。
- Rank 返回陣列維度數
- Length 返回陣列長度(陣列中所有元素的個數)
陣列是引用型別,與所有引用型別一樣,有資料的引用以及資料物件本身。引用在棧或堆上,而陣列物件本身總在堆上。
儘管陣列總是引用型別,但是陣列元素可以是值型別也可以是引用型別。
- 如果儲存的元素都是值型別,陣列被稱作值型別陣列
- 如果儲存的元素都是引用型別,陣列被稱作引用型別陣列
一維陣列和矩形陣列
一維陣列和矩形陣列的語法非常相似,因此放在一起闡述。然後再單獨介紹交錯陣列。
宣告一維陣列或矩形陣列
宣告一維陣列或矩形陣列,在型別和變數名稱間使用一對方括號。
矩形陣列宣告示例:
- 可以使用任意多的秩說明符
- 不能在陣列型別區域中放陣列維度長度。秩是陣列型別的一部分,而緯度長度不是型別的一部分
- 陣列聲明後,維度數就固定了。然而,緯度長度直到陣列例項化時才確定
秩說明符 ↓ int[,,] firstArray; //三維整型陣列 int[,] arr1; //二維整型陣列 long[,] arr3; //三維long陣列 long[3,2,6] SecondArray;//編譯錯誤 ↑ 宣告時不允許設定維度長度
和C/C++不同,方括號在基型別後,而不是在變數名稱後。
例項化一維陣列或矩形陣列
要例項化陣列,我們可以使用陣列建立表示式。陣列建立表示式由new運算子構成,後面是基類名稱和一對方括號。方括號中以逗號分隔每個維度的長度。
例:
int[] arr2=new int[4];//包含4個int的一維陣列 MyClass[] maArr=new MyClass[4];//包含4個MyClass引用的一維陣列 int[,,] arr3=new int[3,6,2];//三維陣列,陣列長度是3*6*2=36
與物件建立表示式不一樣,陣列建立表示式不包含圓括號-即使是對於引用型別陣列。
訪問陣列元素
在陣列中使用整型值作為索引來訪問陣列元素。
- 每個維度的索引從0開始
- 方括號內的索引在陣列名稱之後
int[] intArr1=new int[15]; intArr1[2]=10; int var1=intArr1[2]; int[,] intArr2=new int[5,10]; intArr2[2,3]=7; int var2=intArr2[2,3];
int[] myIntArray; myIntArray=new int[4]; for(int i=0;i<4;i++) { myIntArray[i]-i*10; } for(int i=0;i<4;i++) { Console.WriteLine("Value of element {0} = {1}",i,myIntArray[i]); }
初始化陣列
陣列被建立後,每個元素被自動初始化為型別的預設值。對於預定義型別,整型預設值是0,浮點型預設值是0.0,布林型預設值是false,而引用型別預設值則是null。
例:一維陣列的自動初始化
int[] intArr=new int[4];
顯式初始化一維陣列
- 初始值必須以逗號分隔,並封閉在一組大括號內
- 不必輸入陣列長度,因為編譯器可以通過初始化值的個數來推斷長度
- 注意,在陣列建立表示式和初始化列表中間沒有分隔符。即,沒有等號或其他連線運算子
int[] intArr=new int[]{10,20,30,40};
顯式初始化矩形陣列
要顯式初始化矩形陣列,需遵守以下規則:
- 每個初始值向量必須封閉在大括號內
- 每個維度也必須巢狀並封閉在大括號內
- 除了初始值,每個維度的初始化列表和組成部分也必須使用逗號分隔
int[,] intArray2=new int[,]{{10,1},{2,10},{11,9}};
快捷語法
在一條語句中使用宣告、陣列建立和初始化時,可以省略語法的陣列建立表示式部分。快捷語法如下:
int[] arr1=new int[3]{10,20,30}; int[] arr1= {10,20,30}; int[,] arr=new int[2,3]{{0,1,2},{10,11,12}}; int[,] arr= {{0,1,2},{10,11,12}};
隱式型別陣列
上面宣告的陣列都是顯式指定陣列型別。然而,和其他區域性變數一樣,陣列可以是隱式型別的。
- 當初始化陣列時,我們可以讓編譯器根據初始化語句的型別來推斷陣列型別。只要所有初始化語句能隱式轉換為單個型別,就可以這麼做
- 和隱式型別的區域性變數一樣,使用var關鍵字
雖然用var替代了顯式陣列型別,但是仍然需要在初始化中提供秩說明符。
int[] intArr1=new int[]{10,20,30,40}; var intArr2=new []{10,20,30,40}; int[,] intArr3=new int[,]{{10,1},{2,10},{11,9}}; var intArr4=new [,]{{10,1},{2,10},{11,9}}; string[] sArr1=new string[]{"life","liberty","pursuit of happiness"}; var sArr2=new []{"life","liberty","pursuit of happiness"};
綜合內容
例:綜合示例
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("Element [{0},{1}] is {2}",i,j,arr[i,j]); } }
交錯陣列
交錯陣列是陣列的陣列。與矩形陣列不同,交錯陣列的子陣列的元素個數可以不同。
例:二維交錯陣列
- 第一個維度長度是3
- 宣告可以讀作“jagArr是3個int陣列的陣列”
- 圖中有4個數組物件-其中一個針對頂層陣列,另外3個針對子陣列
int[][] jagArr=new int[3][];
宣告交錯陣列
交錯陣列的宣告語法要求每個維度都有一對獨立的方括號。陣列變數宣告中的方括號數決定了陣列的秩。
和矩形陣列一樣,維度長度不能包括在陣列型別的宣告部分。
int[][] SomeArr;//秩等於2 int [][][] OhterArr;//秩等於3
快捷例項化
我們可以將陣列建立表示式建立的頂層陣列和交錯陣列的宣告相結合。
int[][] jagArr=new int[3][];//3個子陣列
不能在宣告語句中初始化頂層陣列之外的陣列。
int[][] jagArr=new int[3][4];//不允許,編譯錯誤
例項化交錯陣列
和其他型別陣列不一樣,交錯陣列的完全初始化不能在一個步驟中完成。由於交錯陣列是獨立陣列的陣列-每個陣列必須獨立建立。
- 首先,例項化頂層陣列
- 其次,分別例項化每個子陣列,把新建陣列的引用賦給它們所屬陣列的合適元素
例:例項化交錯陣列
int[][] Arr=new int[3][]; Arr[0]=new int[]{10,20,30}; Arr[1]=new int[]{40,50,60,70}; Arr[3]=new int[]{80,90,100,110,120};
比較矩形陣列和交錯陣列
矩形陣列和交錯陣列的結構區別非常大。
例如,下圖演示了3X3的矩形陣列以及由3個長度為3的一維陣列構成的交錯陣列的結構。
- 兩個陣列都儲存9個整數,但是它們結構很不相同
- 矩形陣列只有1個數組物件,而交錯陣列有4個數組物件
在CIL(Common Intermediate Laguage,公共中間語言)中,一維陣列有特定的指令用於效能優化。矩形陣列沒有這些指令,並且不在相同級別進行優化。因此,有時使用一維陣列(可以被優化)的交錯陣列比矩形陣列(不能被優化)更有效率。
另一方面,矩形陣列的程式設計複雜度要小得多,因為它會被作為一個單元而不是陣列的陣列。
foreach語句
foreach語句允許我們連續訪問陣列中的每個元素。
有關foreach語句的重點如下:
- 迭代變數是臨時的,並且和陣列中元素的型別相同。foreach語句使用迭代變數來相繼表示陣列中的每個元素
- foreach語句的語法如下
- Type是陣列中元素的型別。我們可以顯式提供它的型別,或者使用var
- Identifier是迭代變數名
- ArrayName是陣列名
- Statement是要為陣列中每個元素執行一次的單條語句或語句塊
foreach(Type Identifier in ArrayName)//顯式 { Statement } foreach(var Identifier in ArrayName)//隱式 { Statement }
foreach如下方式工作:
- 從陣列第一個元素開始並把它賦值給迭代變數
- 執行語句主體。在主體中,我們可以把迭代變數作為陣列元素的只讀別名
- 主體執行後,foreach語句選擇陣列中的下一個元素並重復處理
例:foreach示例
int[] arr1={10,11,12,13}; foreach(int item in arr1) { Console.WriteLine("Item Value: {0}",item); }
迭代變數是隻讀的
迭代變數是隻讀的。但是,對於值型別陣列和引用型別陣列效果不一樣。
對於值型別陣列,這意味著在用迭代變數時,我們不能改變它們。
例:嘗試修改迭代變數報錯
int[] arr1={10,11,12}; foreach(int item in arr1) { item++; }
對於引用變數,迭代變數儲存的是資料的引用,而不是資料本身。所以,我們雖然不能改變引用,但是我們可以改變資料。
例:引用變數修改迭代變數
class MyClass { public int MyField=0; } class Program { static void Main() { MyClass[] mcArray=new MyClass[4]; for(int i=0;i<4;i++) { mcArray[i]=new MyClass(); mcArray[i].MyField=i; } foreach(var item in mcArray) { item.MyField+=10; } foreach(var item in mcArray) { Console.WriteLine("{0}",item.MyField); } } }
foreach語句和多維陣列
多維數組裡,元素處理順序是從最右邊的索引號依次遞增。
例:矩形陣列示例
class Program { static void Main() { int total=0; int[,] arr1={{10,11},{12,13}}; foreach(var element in arr1) { total+=element; Console.WriteLine("Element:{0},Current Total:{1}",element,total); } } }
例:交錯陣列示例
class Program { static void Main() { int total=0; int[][] arr1=new int[2][]; arr1[0]=new int[]{10,11}; arr1[1]=new int[]{12,13,14}; foreach(int[] array in arr1) { Console.WriteLine("Starting new array"); foreach(int item in array) { total+=element; Console.WriteLine("Item:{0},Current Total:{1}",item,total); } } } }
陣列協變
某些情況下,即使某物件不是陣列的基型別,我們也可以把它賦給陣列元素。這種屬性叫做陣列協變(array covariance)。在下面情況下使用陣列協變。
- 陣列是引用型別陣列
- 在賦值的物件型別和陣列基類之間有隱式轉換或顯式轉換
由於派生類和基類之間總有隱式轉換,因此總是可以把派生類物件賦值給基類宣告的陣列。
例:派生類、基類 陣列協變
class A{...} class B:A{...} class Program { static void Main() { A[] AArray1=new A[3]; A[] AArray2=new A[3]; AArray1[0]=new A();AArray1[1]=new A();AArray1[2]=new A(); AArray2[0]=new B();AArray2[1]=new B();AArray2[2]=new B(); } }
值型別陣列沒有協變
陣列繼承的有用成員
C#陣列從System.Array類繼承。它們從基類繼承了很多有用的屬性和方法。
public static void PrintArray(int[] a) { foreach(var x in a) { Console.WriteLine("{0}",x); } Console.WriteLine(""); } static void Main() { 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 = {0},Length = {1}",arr.Rank,arr.Length); Console.WriteLine("GetLength(0) = {0}",arr.GetLength(0)); Console.WriteLine("GetType() = {0}",arr.GetType()); }
Clone方法
Clone方法為陣列進行淺複製。即,它只建立了陣列本身的克隆。如果是引用型別陣列,它不會複製元素引用的物件。對於值型別陣列和引用型別陣列,效果不同。
- 克隆值型別陣列會產生兩個獨立陣列
- 克隆引用型別陣列會產生指向相同物件的兩個陣列
Clone方法返回object型別的引用,它必須被強制轉換成陣列型別。
int[] intArr1={1,2,3}; int[] intArr2=(int[])intArr1.Clone();
例:Clone值型別陣列示例
class Program { static void Main() { int[] intArr1={1,2,3}; int[] intArr2=(int[])intArr1.Clone(); intArr2[0]=100; intArr2[1]=200; intArr2[2]=300; } }
例:Clone引用型別陣列示例
class A { public int Value=5; } class Program { static void Main() { A[] AArray1=new A[3]{new A(),new A(),new A()}; A[] AArray2=(A[])AArray1.Clone(); AArray2[0].Value=100; AArray2[1].Value=200; AArray2[2].Value=300; } }
比較陣列型別
下表總結了3中型別陣列的重要相似點和不同點。
from: http://www.cnblogs.com/moonache/p/6255327.html