C#入门经典 -- 11.1 集合

    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));

   

posted @ 2008-04-14 11:41  清香的工夫茶  阅读(896)  评论(0编辑  收藏  举报