使用集合

  System.Collections 名称空间中的类 System.Collections.ArrayList 也实现了 IList、ICollection 和 IEnumerable 接口,但实现方式比 System.Array 更复杂。数组的大小是固定不变的(不能添加或删除元素),而这个类可以用于表示大小可变的项列表。为了更准确地理解这个高级集合的功能,下面列举一个使用这个类和一个简单数组的示例。

  数组和高级集合:Ch11Ex01

  (1)在 C:\BegVCSharp\Chapter11 目录中创建一个新的控制台应用程序 Ch11Ex01

  (2)在 解决方案资源管理器 窗口中右击项目,选择 Add | Class 选项,给项目添加 3 个新类: AnimalCowChicken

  (3)修改 Animal.cs 中的代码,如下所示:

    namespace Ch11Ex01
    {
        public abstract class Animal
        {
            protected string name;
            public string Name
            {
                get {
                    return name;
                }
                set {
                    name = value;
                }
            }

            public Animal()
            {
                name = "The animal with no name";
            }

            public Animal(string newName)
            {
                name = newName;
            }

            public void Feed()
            {
                Console.WriteLine("{0} has been fed.", name);
            }
        }
    }

  (4)修改 Cow.cs 中的代码,如下所示:

    namespace Ch11Ex01
    {
        public class Cow : Animal
        {
            public void Milk()
            {
                Console.WriteLine("{0} has been milked.", name);
            }

            public Cow(string newName) : base(newName)
            {
            }
        }
    }

  (5)修改 Chicken.cs 中的代码,如下所示:

    namespace Ch11Ex01
    {
        public class Chicken : Animal
        {
            public void LayEgg()
            {
                Console.WriteLine("{0} has laid an egg.", name);
            }

            public Chicken(string newName) : base(newName)
            {
            }
        }
    }

  (6)修改 Program.cs 中的代码,如下所示:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using Syste.Threading.Tasks;

    namespace Ch11Ex01
    {
        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();
                Cow myCow2 = new Cow("Hayley");
                animalArrayList.Add(myCow2);
                animalArrayList.Add(new Chicken("Roy"));

                foreach (Animal myAnimal in animalArrayList)
                {
                    Console.WriteLine("New {0} object added to ArrayList collection, " + 
                                      "Name = {1}", myAnimal.ToString(), myAnimal.Name);
                }
                Console.WriteLine("ArrayList collection contains {0} objects.", animalArrayList.Count);
                ((Animal)animalArrayList[0]).Feed();
                ((Chicken)animalArrayList[1]).LayEgg();
                Console.WriteLine();

                Console.WriteLine("Additional 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 类是抽象类,所以不能进行实例化。但通过多态性(详见第 8 章),可以使集合中的项成为派生于 Animal 类的 CowChicken 类实例。

  在 Class1.csMain() 方法中创建好这些数组后,就可以显示其特性和功能。有几个处理操作可以应用到 ArrayArrayList 集合上,但它们的语法略有区别。也有一些操作只能使用更高级的 ArrayList 类型。

  下面首先通过比较这两种集合类型的代码和结果,讨论一下类似操作。首先是集合的创建。对于简单数组而言,只有固定的大小来初始化数组,才能使用它。下面使用第 5 章介绍的标准语法创建数组 animalArray

    Animal[] animalArray = new Animal[2];

  这个类还有另外两个构造函数。第一个构造函数把现有的集合作为一个参数,将其内容复制到新实例中;而另一个构造函数通过一个参数设置集合的容量(capacity)。这个容量用一个int值指定,设置集合中可以包含的初始项数。但这并不是绝对容量,因为如果集合中的项数超过了这个值,容量就会自动增加一倍。

  因为数组是引用类型(例如,AnimalAnimal 派生的对象),所以用一个长度初始化数组并没有初始化它所包含的项。要使用一个指定的项,该项还需要初始化,即需要给这个项赋予初始化了的对象:

    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("Roy"));

  除语法稍有不同外,还可以采用相同的方式把新对象或现有对象添加到集合中。以这种方式添加完项后,就可以使用与数组相同的语法来改写它们,例如:

    animalArrayList[0] = new Cow("Anima");

  但不能在这个示例中这么做。

  第 5 章介绍了如何使用 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 对象也支持 IEnumerable 接口,并可以与 foreach 一起使用,此时语法是相同的:

    foreach (Animal myAnimal in animalArrayList)
    {
        Console.WriteLine("New {0} object added to ArrayList 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 属性为 HayleyCow 对象:

    animalArrayList.RemoveAt(0);

  另外,还可以使用:

    animalArrayList.Remove(myCow2);

  因为这个对象已经有一个本地引用了,所以可以通过 Add() 添加对数组的一个现有引用,而不是创建一个新对象。无论采用哪种方式,集合中唯一剩余的项是 Chicken对象,可以通过以下方式访问它:

    ((Animal)animalArrayList[0]).Feed();

  当对 ArrayList 对象中的项进行修改,使数组中剩下 N 个项时,其索引范围为 0 ~ N-1。例如,删除索引为 0 的项,会使其他项在数组中移动一个位置,所以应使用索引 0(而非 1)来访问 Chicken 对象。不再有索引为 1 的项了(因为集合中最初只有两个项),所以如果试图执行下面的代码,就会抛出异常:

    ((Animal)animalArrayList[1]).Feed();

  ArrayList 集合可以用 AddRange() 方法一次添加好几项。这个方法接受带有 ICollection 接口的任意对象,包括前面的代码所创建的 animalArray 数组:

    animalArrayList.AddRange(animalArray);

  为了确定这是否有效,可以试着访问集合中的第三项,它将是 animalArray 中的第二项:

    ((Chicken)animalArrayList[2]).LayEgg();

  AddRange() 方法不是 ArrayList 提供的任何接口的一部分。这个方法专用于 ArrayList 类,证实了可以在集合类中执行定制操作,而不仅是前面介绍的接口要求的操作。这个类还提供了其他有趣的方法,如 InsertRange(),它可以把数组对象插入到列表中的任何位置,还有用于排序和重新排序数组的方法。

  最后,再回头来看看对同一个对象进行多个引用。使用 IList 接口中的 IndexOf() 方法可以看出, myCow1 (最初添加到 animalArray 中的一个对象)现在是 animalArrayList 集合的一部分,它的索引如下:

    Console.WriteLine("The animal called {0} is at index {1}.", myCow.Name, animalArrayList.IndexOf(myCow1));

  例如,接下来的两行代码通过对象引用重新命名了对象,并通过集合引用显示了新名称:

    myCow1.Name = "Janice";
    Console.WriteLine("The animal is now called {0}.", ((Animal)animalArrayList[1]).Name);

🔚