C#中的數組實現為System.Array類的執行個體,它們只是集合類中的一種。集合類一般用於處理對象列表,其功能比簡單數組要多,這些功能是通過實現System.Collections命名空間中的介面而獲得的,因此介面的文法已經標準化了。
集合的功能(包括基本函數,例如用[index]文法訪問集合中的項。)可以通過介面來實現,該介面不僅沒有限制我們使用基本集合類,例如System.Array,相反,我們還可以建立自己的定製集合類。這些集合可以專用於要枚舉的對象(即要從中建立集合的對象)。這麼做的一個優點是定製的集合類可以是強型別化的。也就是說,從集合中提取項時,不需要把它們轉換為正確的類型。另一個優點是提供專門的方法,例如,可以提供獲得項子集的快捷方法。
在System.Collections命名空間中有許多介面都提供了基本的集合功能:
- IEnumerable可以迭代集合中的項。
- ICollection(繼承於IEnumerable)可以擷取集合中項的個數,並能把項複製到一個簡單的數群組類型中。
- IList(繼承於ICollection)提供了集合的項列表,並可以訪問這些項,以及其他一些與項列表相關的功能。
- IDictionary(繼承於ICollection)類似於IList,但提供了可通過鍵碼值而不是索引訪問的項列表。
System.Array類實現了IList、ICollection和IEnumerable,但不支援IList的一些更進階的功能,表示大小固定的項列表。
11.1.1 使用集合
System.Collections命名空間中的一個類System.Collections.ArrayList也實現了IList、ICollection和IEnumerable介面,但實現的方式比System.Array更複雜。
【樣本】
類Animal
1: namespace Ch11Ex01
2: {
3: class Animal
4: {
5: protected string name;
6:
7: public string Name
8: {
9: get
10: {
11: return name;
12: }
13: set
14: {
15: name = value;
16: }
17: }
18:
19: public Animal()
20: {
21: name = "The animal with no name";
22: }
23:
24: public Animal(string newName)
25: {
26: name = newName;
27: }
28:
29: public void Feed()
30: {
31: Console.WriteLine("{0} has been feed.",name);
32: }
33: }
34: }
類Cow
1: namespace Ch11Ex01
2: {
3: class Cow:Animal
4: {
5: public void Milk()
6: {
7: Console.WriteLine("{0} has ben milked.",name);
8: }
9:
10: public Cow(string name)
11: : base(name)
12: {
13:
14: }
15: }
16: }
類Chicken
1: class Chicken: Animal
2: {
3: public void LayEgg()
4: {
5: Console.WriteLine("{0} has laid an egg.",name);
6: }
7:
8: public Chicken(string name):base(name)
9: {
10:
11: }
12: }
Program代碼:
class Program { static void Main(string[] args) { Console.WriteLine("Create an Array type collection of Animal objects and use it."); Animal[] animalArray = new Animal[2]; Cow myCow1 = new Cow("Deirdre"); animalArray[0] = myCow1; animalArray[1] = new Chicken("Ken"); foreach (Animal myAnimal in animalArray) { Console.WriteLine("New {0} object added to Array collection,Name = {1}",myAnimal.ToString(),myAnimal.Name); } Console.WriteLine("Array collection contains {0} objects.",animalArray.Length); animalArray[0].Feed(); ((Chicken)animalArray[1]).LayEgg(); Console.WriteLine(); Console.WriteLine("Create an ArrayList type collection of Animal objects and use it."); ArrayList animalArrayList = new ArrayList(); animalArrayList.Add(new Cow("Hayley")); animalArrayList.Add(new Chicken("Rory")); foreach (Animal myAnimal in animalArrayList) { Console.WriteLine("New {0} object added to Array collection,Name = {1}",myAnimal.ToString(),myAnimal.Name); } ((Cow)animalArrayList[0]).Feed(); ((Chicken)animalArrayList[1]).LayEgg(); Console.WriteLine(); Console.WriteLine("Addition manipulation of ArrayList:"); animalArrayList.RemoveAt(0); ((Animal)animalArrayList[0]).Feed(); animalArrayList.AddRange(animalArray); ((Chicken)animalArrayList[2]).LayEgg(); Console.WriteLine("The animal called {0} is at index {1}",myCow1.Name,animalArrayList.IndexOf(myCow1)); myCow1.Name = "Janice"; Console.WriteLine("The animal is now called {0}",((Animal)animalArrayList[1]).Name); Console.ReadKey(); } }
運行結果如下:
【樣本說明】
這個樣本建立了兩個對象集合,第一個集合使用System.Array(這是一個簡單的數組),第二個集合使用
System.Collections.ArrayList類。這兩個集合都是Animal對象,在Animal.cs中定義。Animal類是抽象類別,所以不能進行
執行個體化。但通過多態性,可以使用集合中項成為派生於Animall類的Cow和Chicken類執行個體。
下面首先通過比較這兩種集合的代碼和結果,討論一下這兩種集合的類似操作。
首先是集合的建立。對於簡單的數組來說,必須用固定的大小來初始化數組,才能使用它。
Animal[] animalArray = new Animal[2];
而ArrayList集合不需要初始化其大小,所以可以使用下面的代碼建立列表anmialArrayList:
ArrayList animalArrayList = new ArrayList();
這個類還有另外兩個建構函式。第一個建構函式把現有的集合作為一個參數,把現有集合的內容複寫到新執行個體中;而另一個建構函式通過一個參數設定集合的容量(capactiy)。這個容量用一個int值指定,設定集合中可以包含的項數。但是這並不是真實的容量,因為如果集合中的項數超過了這個值,容量就會自動增大一倍。
因為數組是參考型別的,所以用一個長度初始化數組並沒有初始化它所包含的項。要使用一個指定的項,該項還需要初始化,即需要給這個項賦予初始化了的對象:
Cow myCow1 = new Cow("Deirdre");
animalArray[0] = myCow1;
animalArray[1] = new Chicken("Ken");
這段代碼以兩種方式完成該初始化任務:用現有的Cow對象來賦值,或者通過建立一個新的Chicken對象來賦值。主要區別是前者引用了數組中的對象。
對於ArrayList集合,它沒有現成的項,也沒有null引用的項。這樣就不能以相同的方式給索引賦予新執行個體。我們使用ArrayList對象的Add()方法添加新項:
Cow myCow2 = new Cow("Hayley");
animalArrayList.Add(myCow2);
animalArrayList.Add(new Chicken("Rory"));
除了文法略有不同外,還可以用相同的方式把新對象或現有的對象添加到集合中。
以這種方式添加完項後,就可以使用與數組相同的文法來重寫它們,例如:
animalArrayList[0] = new Cow("Alma");
可以使用foreach結構迭代一個數組,因為System.Array類實現了IEnumerable介面,這個介面的唯一方法GetEnumerator()可以迭代集合中的項目。在代碼中,我們寫出了數組中的每個Animal對象的資訊:
foreach (Animal myAnimal in animalArray)
{
Console.WriteLine("New {0} object added to Array collection,Name = {1}",myAnimal.ToString(),
myAnimal.Name);
}
這裡使用的ArrayList對象也支援IEumerable介面;並可以與foreach一起使用,此時文法是相同的。
foreach (Animal myAnimal in animalArrayList)
{
Console.WriteLine("New {0} object added to Array collection,Name = {1}",myAnimal.ToString(),
myAnimal.Name);
}
接著,使用數組的Length屬性,在螢幕上輸出數組中項的個數:
Console.WriteLine("Array collection contains {0} objects.",animalArray.Length);
也可以使用ArrayList集合得到相同的結果,但要使用Count屬性,該屬性是ICollection介面的一部分:
Console.WriteLine("ArrayList collection contains {0} objects.",animalArrayList.Count);
簡單數組是強型別化的,可以直接存取它們所包含的項類型。所以可以直接調用項的方法:
animalArray[0].Feed();
數組的類型是抽象類別型Animal,因此不能直接調用有衍生類別提供的方法,而必須使用資料類型轉換:
((Chicken)animalArray[1]).LayEgg();
ArrayList集合是System.Object對象的集合(通過多態性賦給Animal對象),所以必須對所有的項進行資料類型轉換:
((Animal)animalArrayList[0]).Feed();
((Chicken)animalArrayList[1]).LayEgg();
代碼的剩餘部分利用的一些ArrayList集合功能超出了Array集合的功能範圍。
首先,可以使用Remove()和RemoveAt()方法刪除項,這兩個方法是在ArrayList類中實現的IList介面的一部分。它們分別根據項目引用或索引把項目從數組中刪除。在本例中,我們使用後一個方法刪除添加在到列表中的第一個項,即Name屬性為Hayley的Cow對象:
animalArrayList.RemoveAt(0);
另外,還可以使用:
animalArrayList.Remove(myCow2);
無論採用哪種方式,集合中唯一剩下的項是Chicken對象,可以用下面的方式訪問它:
((Animal)animalArrayList[0]).Feed();
對ArrayList對象中的項進行修改,使數組中剩下N個項,其實現方式與保留0~N-1的索引相同。例如,刪除索引為0的項,會使其他項在數組中移動一個位置,所以應使用索引0來訪問Chicken對象,而不是1。不再有索引為1的項了(因為集合中最初只有兩個項),所以如果試圖執行下面的代碼,就會拋出異常:
((Animal)animalArrayList[1]).Feed();
ArrayList集合可以用AddRange()方法一次添加好幾個項。這個方法接受帶有ICollection介面的任何對象,該介面包含前面的代碼所建立的animalArray數組:
animalArrayList.AddRange(animalArray);
AddRange()方法不是ArrayList提供的任何介面的一部分。這個方法專用於ArrayList類,論證了可以在集合類中執行定製操作,而不僅僅是前面介紹的介面要求的操作。這個類還提供了其他有趣的方法,例如InsterRange(),它可以把數組對象插入到列表中的任何位置,還有用於給數組排序和重新排序的方法。
最後,再回頭來看看對同一個對象進行多個引用。使用IList介面中的IndexOf()方法可以看出,myCow1(最初添加到animalArray中的一個對象)現在是animalArrayList集合中的一部分,它的索引如下:
Console.WriteLine("The animal called {0} is at index {1}.",myCow1.Name,animalArrayList.IndexOf(myCow1));
例如,接下來的兩行代碼通過對象引用重新命名了對象,並通過集合引用顯示了新名稱:
myCow1.Name = "Janice";
Console.WriteLine("The animal is now called {0}.",((Animal)animalArrayList[1].Name));