17 個陣列
17.1 一般
陣列是一種資料結構,其中包含一些可透過計算索引存取的變數。 陣列中包含的變數 (也稱為陣列的元素) 屬於相同的型別,這種型別稱為陣列的元素型別。
陣列有一個排名,可決定與每個數位專案相關聯的索引數目。 陣列的排名也稱為陣列的維度。 排名為一的陣列稱為 單一維度陣列。 排名大於一的陣列稱為 多維度陣列。 特定大小的多維度陣列通常稱為二維陣列、三維數位等等。 陣列的每個維度都有一個相關聯的長度,這個長度是大於或等於零的整數。 維度長度不是數位類型的一部分,而是在運行時間建立數位類型的實例時建立。 維度的長度決定該維度的有效索引範圍:對於長度 N
的維度,索引的範圍可以是從 0
到 N – 1
內含的。 陣列中的元素總數是陣列中每個維度長度的乘積。 如果陣列的一或多個維度長度為零,則表示陣列是空的。
陣列的元素類型本身可以是數位類型(~17.2.1)。 陣列的這類陣列與多維度陣列不同,可用來表示「不規則陣列」。
範例:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
end 範例
每個數位類型都是參考類型 (~8.2)。 陣列的元素類型可以是任何類型,包括實值型別和數位類型。
17.2 數位型態
17.2.1 一般
數位類型的文法生產專案是在 •8.2.1 中提供。
數位類型會寫入為 non_array_type ,後面接著一或多個 rank_specifier。
non_array_type是本身不是array_type的任何類型。
陣列類型的排名是由array_type中最左邊的rank_specifier所指定:rank_specifier表示陣列是一個陣列,其排名為一個加上rank_specifier中的 “,
” 標記數目。
陣列類型的元素類型是刪除最 左邊rank_specifier所產生的類型:
- 表單
T[R]
的數位類型是具有 rankR
和非陣列項目類型的T
陣列。 - 表單
T[R][R₁]...[Rₓ]
的數位類型是具有 rankR
和專案類型的T[R₁]...[Rₓ]
數位。
實際上,rank_specifier會從左至右讀取最後的非陣列項目類型。
範例:中的
T[][,,][,]
類型是 的二維陣列之三維陣列的單維陣列int
。 end 範例
在運行時間,陣列類型的值可以是 null
或該陣列類型的實例參考。
注意:遵循 \17.6 的規則,值也可能是 covariant 陣列類型的參考。 end note
17.2.2 System.Array 類型
此類型 System.Array
是所有數位類型的抽象基底類型。 隱含參考轉換 (~10.2.8) 從任何陣列類型到 System.Array
實作 System.Array
的任何介面類型存在。 明確參考轉換 (~10.3.5) 存在, System.Array
且由 實作到任何陣列類型的任何介面 System.Array
類型。 System.Array
本身不是 array_type。 相反地,這是衍生所有array_type的class_type。
在運行時間,型 System.Array
別的值可以是 null
或任何數位類型的實例參考。
17.2.3 數位和泛型集合介面
單一維度陣列 T[]
會實作 介面 System.Collections.Generic.IList<T>
(IList<T>
簡稱),以及其基底介面。 因此,有從 到 IList<T>
及其基底介面的隱含轉換T[]
。 此外,如果有從 S
到 T
的隱含參考轉換,S[]
則會實作 IList<T>
,而且有從 到 IList<T>
及其基底介面的隱含參考轉換 S[]
(~10.2.8)。 如果有從 S
到 T
的明確參考轉換,則有從 到 IList<T>
及其基底介面的明確參考轉換 S[]
(~10.3.5)。
同樣地,單維陣列 T[]
也會實作 介面 System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
簡稱),以及其基底介面。 因此,有從 到 IReadOnlyList<T>
及其基底介面的隱含轉換T[]
。 此外,如果有從 S
到 T
的隱含參考轉換,S[]
則會實作 IReadOnlyList<T>
,而且有從 到 IReadOnlyList<T>
及其基底介面的隱含參考轉換 S[]
(~10.2.8)。 如果有從 S
到 T
的明確參考轉換,則有從 到 IReadOnlyList<T>
及其基底介面的明確參考轉換 S[]
(~10.3.5)。
範例:例如:
class Test { static void Main() { string[] sa = new string[5]; object[] oa1 = new object[5]; object[] oa2 = sa; IList<string> lst1 = sa; // Ok IList<string> lst2 = oa1; // Error, cast needed IList<object> lst3 = sa; // Ok IList<object> lst4 = oa1; // Ok IList<string> lst5 = (IList<string>)oa1; // Exception IList<string> lst6 = (IList<string>)oa2; // Ok IReadOnlyList<string> lst7 = sa; // Ok IReadOnlyList<string> lst8 = oa1; // Error, cast needed IReadOnlyList<object> lst9 = sa; // Ok IReadOnlyList<object> lst10 = oa1; // Ok IReadOnlyList<string> lst11 = (IReadOnlyList<string>)oa1; // Exception IReadOnlyList<string> lst12 = (IReadOnlyList<string>)oa2; // Ok } }
指派
lst2 = oa1
會產生編譯時間錯誤,因為 從object[]
IList<string>
轉換成 是明確的轉換,而不是隱含的。 轉換(IList<string>)oa1
會導致在運行時間擲回例外狀況,因為oa1
參考object[]
,而不是string[]
。 不過,轉換不會IList<string>)oa2
擲回例外狀況,因為oa2
參考 了string[]
。end 範例
每當有從 到 的隱含或明確參考轉換S[]
時,也會有來自 的明確參考轉換,其基底介面會S[]
轉換成 IList<T>
(~10.3.5)。IList<T>
當陣列類型 S[]
實作 IList<T>
時,實作介面的某些成員可能會擲回例外狀況。 介面實作的精確行為超出此規格的範圍。
17.3 陣列建立
陣列實例是由 array_creation_expression s ({12.8.16.5) 或欄位或局部變數宣告所建立,其中包含array_initializer ({17.7)。 陣列實例也可以隱含地建立,作為評估涉及參數陣列的自變數清單的一部分(~15.6.2.4)。
建立陣列實例時,會建立每個維度的排名和長度,然後在實例的整個存留期內維持常數。 換句話說,無法變更現有陣列實例的排名,也無法調整其維度的大小。
陣列實例一律為陣列類型。 此 System.Array
類型是無法具現化的抽象型別。
array_creation_expression 所建立陣列的元素一律會初始化為其預設值(~9.3)。
17.4 陣列元素存取
數位元素是使用表單的element_access表示式來存取,其中 A
是陣列類型的運算式,而且每個Iₑ
運算式都是類型int
、uint
long
ulong
、 或 的運算式,可以隱含地轉換成其中一或多個類型。A[I₁, I₂, ..., Iₓ]
陣列專案存取的結果是變數,也就是索引所選取的陣列專案。
您可以使用 語句來列舉 foreach
陣列的專案(~13.9.5)。
17.5 陣列成員
每個數位類型都會繼承型別所 System.Array
宣告的成員。
17.6 陣列共變數
對於任兩個reference_type和 B
A
,如果隱含參考轉換 (~10.2.8) 或明確參考轉換 (~10.3.5) 從 到 B
A
存在,則陣列類型A[R]
也存在相同的參考轉換到陣列類型B[R]
,其中 R
任何指定rank_specifier (但兩個陣列類型的相同)。 此關聯性稱為陣列共變數。 陣列共變數,特別是,表示陣列類型的值實際上可能是數位類型的A[R]
B[R]
實例參考,前提是隱含參考從轉換成 B
A
。
由於陣列共變數,參考型別數位元素的指派包含運行時間檢查,可確保指派給陣列元素的值實際上是允許的類型(~12.21.2)。
範例:
class Test { static void Fill(object[] array, int index, int count, object value) { for (int i = index; i < index + count; i++) { array[i] = value; } } static void Main() { string[] strings = new string[100]; Fill(strings, 0, 100, "Undefined"); Fill(strings, 0, 10, null); Fill(strings, 90, 10, 0); } }
方法中
Fill
對array[i]
的指派會隱含包含運行時間檢查,這可確保value
該檢查是null
與 實際專案型array
別相容的型別對象的參考或參考。 在Main
中,成功的前兩個叫Fill
用,但第三個調用會在System.ArrayTypeMismatchException
執行第一個指派array[i]
給 時擲回 。 發生例外狀況的原因是 Boxedint
無法儲存在陣列中string
。end 範例
陣列共變數特別不會延伸至 value_type的陣列。 例如,沒有允許 int[]
將 視為的 object[]
轉換存在。
17.7 陣列初始化表達式
數位初始化表達式可以在字段宣告 ({15.5)、局部變數宣告 ({13.6.2) 和數位建立表達式中指定 ({12.8.16.5):
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
數位初始化運算式是由變數初始化表達式序列所組成,由 “{
” 和 “}
” 標記括住,並以 “,
” 標記分隔。 每個變數初始化運算式都是表達式,或者,如果是多維度陣列,則為巢狀數位初始化表達式。
使用數位初始化表示式的內容會決定要初始化的陣列類型。 在陣列建立表達式中,數位型別緊接在初始化表達式之前,或是從數位初始化運算式中的運算式推斷。 在欄位或變數宣告中,數位類型是所宣告之字段或變數的類型。 當陣列初始化表示式用於字段或變數宣告時,
int[] a = {0, 2, 4, 6, 8};
它只是對等陣列建立表達式的速記:
int[] a = new int[] {0, 2, 4, 6, 8};
對於單一維度陣列,陣列初始化運算式應該包含一連串的運算式,每個運算式都會隱含轉換成陣列的元素類型(~10.2)。 表達式會以遞增順序初始化數位元素,從索引為零的項目開始。 數位初始化運算式中的運算式數目會決定要建立的數位實例長度。
範例:上述陣列初始化表達式會
int[]
建立長度為 5 的實例,然後使用下列值初始化實例:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
end 範例
對於多維度陣列,陣列初始化表達式的巢狀層級應該與陣列中有維度一樣多。 最外層的巢狀層級會對應至最左邊的維度,而最內層的巢狀層級會對應至最右邊的維度。 數位的每個維度長度取決於數位初始化運算式中對應巢狀層級的項目數目。 對於每個巢狀陣列初始化表達式,元素數目應與相同層級的其他數位初始化表達式相同。
範例:範例:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
會為最左邊的維度建立長度為 5 的二維陣列,而最右邊維度的長度為 2:
int[,] b = new int[5, 2];
然後使用下列值初始化陣列實例:
b[0, 0] = 0; b[0, 1] = 1; b[1, 0] = 2; b[1, 1] = 3; b[2, 0] = 4; b[2, 1] = 5; b[3, 0] = 6; b[3, 1] = 7; b[4, 0] = 8; b[4, 1] = 9;
end 範例
如果以長度為零指定最右邊的維度,則會假設後續維度的長度為零。
範例:
int[,] c = {};
會為最左邊和最右邊的維度建立長度為零的二維陣列:
int[,] c = new int[0, 0];
end 範例
當陣列建立表達式同時包含明確的維度長度和數位初始化運算式時,長度應該是常數表達式,而且每個巢狀層級的項目數目應符合對應的維度長度。
範例:以下是一些範例:
int i = 3; int[] x = new int[3] {0, 1, 2}; // OK int[] y = new int[i] {0, 1, 2}; // Error, i not a constant int[] z = new int[3] {0, 1, 2, 3}; // Error, length/initializer mismatch
在這裡,的
y
初始化表達式會產生編譯時間錯誤,因為維度長度表達式不是常數,而初始化表達式會產生z
編譯時間錯誤,因為初始化表達式中的長度和專案數目不一致。end 範例
注意:C# 允許在array_initializer結尾加上逗號。 此語法可讓您彈性地從這類清單中新增或刪除成員,並簡化這類清單的機器產生。 end note