第11章 集合、比较和转换(C#入门经典第6版)


1、集合

      数据有一定的限制,最不能忍受的是一旦创建,数组的大小就固定,不能再添加。而集合则包含了数组所具有的功能,且可以任意添加/删减元素项,还有一些其他 功能。

  集合的功能主要通过接口来实现,接口包含在System.Collections命名空间中。

  主要有:

  • IEnumerable 可以迭代集合中的元素项
  • ICollection(继承于IEnumerable)可以获得集合中元素项的个数,并能把元素项复制到一个简单的数组类型中。
  • IList(继承于IEnumerable和ICollection)提供了集合的元素项列表,允许访问这些项,并提供其他一些与元素项列表相关的基本功能。
  • IDictionary(继承于IEnumerable和ICollection)类似于IList,但提供了可通过健值访问的元素项列表。

     (1)System.Array类与System.Collections.ArrayList类

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Console.WriteLine("Create an Array type collection of Animal " +
 6                 "objects and use it:");
 7             Animal[] animalArray = new Animal[2];
 8             Cow myCow1 = new Cow("Deirdre");
 9             animalArray[0] = myCow1;
10             animalArray[1] = new Chicken("Ken");
11 
12             foreach (Animal myAnimal in animalArray)
13             {
14                 Console.WriteLine("New {0} object added to Array collection, " +
15                     "Name = {1}", myAnimal.ToString(), myAnimal.Name);
16             }
17 
18             Console.WriteLine("Array collection contains {0} objects.", animalArray.Length);
19             animalArray[0].Feed();
20             ((Chicken)animalArray[1]).LayEgg();
21             Console.WriteLine();
22 
23             Console.WriteLine("Create an ArrayList type collection of Animal " +
24                 "objects and use it:");
25             ArrayList animalArrayList = new ArrayList();
26             Cow myCow2 = new Cow("Hayley");
27             animalArrayList.Add(myCow2);
28             animalArrayList.Add(new Chicken("Roy"));
29 
30             foreach (Animal myAnimal in animalArrayList)
31             {
32                 Console.WriteLine("New {0} object added to ArrayList collection," +
33                     " Name = {1}", myAnimal.ToString(), myAnimal.Name);
34             }
35             Console.WriteLine("ArrayList collection contains {0} objects.", animalArrayList.Count);
36             ((Animal)animalArrayList[0]).Feed();
37             ((Chicken)animalArrayList[1]).LayEgg();
38             Console.WriteLine();
39 
40             Console.WriteLine("Additional manipulation of ArrayList:");
41             animalArrayList.RemoveAt(0);
42             ((Animal)animalArrayList[0]).Feed();
43             animalArrayList.AddRange(animalArray);
44             ((Chicken)animalArrayList[2]).LayEgg();
45             Console.WriteLine("The animal called {0} is at index {1}.",
46                 myCow1.Name, animalArrayList.IndexOf(myCow1));
47             myCow1.Name = "Janice";
48             Console.WriteLine("The animal is now called {0}.",
49                 ((Animal)animalArrayList[1]).Name);
50             Console.ReadKey();
51         }
52     }

  System.Array类只能在初始化时固定数组大小,如:

  Animal[] animalArray = new Animal[2];

  而System.Collections.ArrayList类则是:

  ArrayList animalArrayList = new ArrayList();//可以设置容量 ,但当超过时会自动增加一倍。

简单数组Array中是强类型的,本例中是Animal类型,而ArrayList中则是System.Object。

System.Array类和ArrayList都实现了IEnumerable接口,因此可以在foreach中迭代。

 (2)定义集合---创建自己的强类型化集合

  一般情况是从一个类中派生自己的集合,例如System.Collections.CollectionBase类,这个抽象类提供了集合类的大量的实现代码,包含了IEnumerable、ICollection、Ilist接口。

  CollectionBase类提供了两个受保护的属性,可以访问存储对象本身。List可以通过IList接口访问项,而InnerList则可用于存储项的ArrayList对象。

 1 public class Animals:CollectionBase
 2     {
 3         public void Add(Animal newAnimal)
 4         {
 5             List.Add(newAnimal);
 6         }
 7         public void Remove(Animal oldAnimal)
 8         {
 9             List.Remove(oldAnimal);
10         }
11         public Animals()
12         {
13 
14         }
15     }

(3)索引符

  上面定义的集合Animals是不能如animalCollection[0].Feed()这样按照索引来访问元素项的,必须使用索引符。

  索引符其实是一种特殊类型的属性,可添加在类中,提供类似于数组的访问。

1  public Animal this[int animalIndex]//this关键字和后边的方括号中的参数一起使用,如MyAnimals[0]。
2         {
3             get { return (Animal)List[animalIndex]; }
4             set { List[animalIndex] = value; }
5         }
 

小结:通过使用CollectionBase创建自己的强类型化集合集中了System.Array类的强类型化和System.Collections.ArrayList的多种项列表操作方法的优点,比较方便。但是内部还是发生了类型转换。

(4)键控集合和IDictionary

  类似于IList接口通过数字索引访问项列表,IDictionary接口通过健值进行索引。同样,也有一个基类实现了IDictionary接口,DictionaryBase。

  

 1 public class Animals:DictionaryBase
 2     {
 3         public void Add(string newID,Animal newAnimal)
 4         {
 5             Dictionary.Add(newID, newAnimal);
 6         }
 7         public void Remove(string animalID)
 8         {
 9             Dictionary.Remove(animalID);
10         }
11         public Animals()
12         { }
13 
14         public Animal this[string animalID]
15         {
16             get { return (Animal)Dictionary[animalID]; }
17             set { Dictionary[animalID] = value; }
18         }
19     }

  但是基于DictionaryBase的集合和基于CollectionBase的集合之间还有一个区别是foreach的工作方式稍有区别。基于CollectionBase的集合可以从集合中直接提取Animal对象,而使用foreach和DictionaryBase的集合则需要提供DictionaryEntry结构才能得到类似的效果。

  

1             foreach (DictionaryEntry myEntry in animalCollection)
2             {
3                 Console.WriteLine("New {0} object added to custom collection, " +
4                     "Name = {1}", myEntry.Value.ToString(), ((Animal)myEntry.Value).Name);
5             }

(5) 迭代器

  迭代器的定义是,它是一个代码块,按顺序提供了要在foreach循环中使用的所有值。迭代器代码块的返回类型有两种,IEnumerable和IEnumerator。

如果要迭代一个类,可使用方法GetEnumerator(),其返回类型是IEnumerator。

如果要迭代一个类成员,则使用IEnumerable。

  这里要使用yield关键字,但书上没有详细介绍。

(6)深度复制

  所谓深度复制和浅度复制,在值类型上感觉是没有区别的,都是复制一个值,而在引用类型上,浅度复制是复制对象的一个相同的引用,副本改变后则源也要改变。但是深度复制则不同,其复制对象的内容(包括字段等)而引用不同,副本发生变化时,源则不变。

 

2、比较

(1)类型比较

  a. 封箱与拆箱

  封箱是把值类型转换为System.Object类型或者转换为由值类型实现的接口类型。包含值类型变量的一个副本的引用。

  拆箱是相反的过程。

  封箱是在没有用户干涉的情况下进行的(即不需要编写任何代码),但是拆箱一个值需要进行显式转换,即需要类型转换。

  b. is运算符

  is运算符并不是用来说明对象是某种类型,而是用来检查对象是不是给定类型,或者是否可以转换为给定类型,如果是,就返回true。

  <operand> is <type>

  •  如果<type>是一个类类型,而<operand>也是该类型,或者它继承了该类型,或者它可以封装到该类型中,则结果为true。
  •    如果<type>是一个接口类型,而<operand>也是该类型,或者它是实现该接口的类型,则结果为true。
  •    如果<type>是一个值类型,而<operand>也是该类型,或者它可以拆箱到该类型中,则结果为true。

(2)值比较

  a. 运算符重载

  要在类中重载运算符,必须给类添加运算符类型成员(使用operator关键字和运算符本身)且必须是static。

  b. 使用IComparable和IComparer接口,该种方式广泛用于集合类中。

  • IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象
  • IComparer在一个单独的类中实现,可以比较任意两个对象

  一般使用IComparable给出类的默认比较代码,使用其他类给出非默认的比较代码。IComparable提供了一个方法CompareTo(),这个方法接受一个对象。IComparer也提供了一个Compare()方法。

  最后,.Net Framework在类Comparer上提供了IComparer接口的默认实现方式,类Comparer位于System.Collections命名空间中,可以对简单类型以及支持IComparable接口的任意类型进行特定文化的比较。

3、转换

(1)重载转换运算符

  在代码中,使用关键字implicit和explicit来指定转换

 1     public class ConvClass1
 2     {
 3         public int val;
 4         public static implicit operator ConvClass2(ConvClass1 op1)
 5         {
 6             ConvClass2 returnVal = new ConvClass2();
 7             returnVal.val = op1.val;
 8             return returnVal;
 9         }
10 
11     }
12     public class ConvClass2
13     {
14         public double val;
15         public static explicit operator ConvClass1(ConvClass2 op1)
16         {
17             ConvClass1 returnVal = new ConvClass1();
18             returnVal.val = (int)(op1.val);
19             return returnVal;
20         }
21     }

 

(2) as运算符

  使用下面的语法,把一种类型转换为指定的引用类型。

  <operand> as <type>

  •  <operand>的类型是<type>类型
  •   <operand>可以隐式转换为<type>类型
  •    <operand>可以封箱到<type>类型中

  如果不能从<operand>转换为<type>,则表达式的结果为null。

   


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM