14.集合
2019-03-20 11:26 若藜520 阅读(208) 评论(0) 编辑 收藏 举报1.System.Collections命名空间中的几个接口
1)IEnumerable可迭代集合中的项;
2)ICollection(继承于IEnumerable)可以获取集合中项的个数,并把项复制到一个简单的数组中;
3)IList(继承于IEnumerable和ICollection)提供了集合的项列表,允许访问这些项,并提供一些与项相关的基本功能;
4)IDictionary(继承于IEnumerable和ICollection)类似IList,但提供了通过键值(而不是索引)访问项的列表;
以上几个接口提供了基本的集合功能。System.Array类实现了IEnumerable、ICollection和IList接口,但不支持IList的一些更高级的功能,它表示大小固定的项列表。C#中的数组是System.Array类的实例。
2.使用数组实现扑克牌集合

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Deck d = new Deck(); for (int i = 0; i < 52; i++) { Console.Write(d.GetCard(i)); } Console.WriteLine(); Console.WriteLine(); d.Shuffle(); for (int i = 0; i < 52; i++) { Console.Write(d.GetCard(i)); } d.GetCard(53); Console.WriteLine(); Console.WriteLine(); Console.ReadLine(); } } class Card { private readonly Suit suit; private readonly Rank rank; public Card(Suit suit,Rank rank) { this.suit = suit; this.rank = rank; } public override string ToString() { return "The "+rank .ToString()+" of "+ suit.ToString()+" s"; } } /// <summary> /// 使用数组实现集合Deck,缺点是集合的大小是固定的 /// </summary> class Deck { private Card[] Cars; public Deck() { Cars = new Card[52]; for (int suitVal = 0; suitVal < 4; suitVal++) { for (int rankVal = 1; rankVal <= 13; rankVal++) { Cars[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal, (Rank)rankVal); } } } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) return Cars[cardNum]; else throw new ArgumentOutOfRangeException("cardNum", cardNum, "Value must between 0 and 51."); } public void Shuffle() { Random sourceGen = new Random(); Card[] newCards = new Card[52]; bool[] assigned = new bool[52]; for (int i = 0; i < 52; i++) { int destCard = 0; bool foundCard = false; while (!foundCard) { destCard = sourceGen.Next(52); if (assigned[destCard] == false) foundCard = true; } assigned[destCard] = true; Cars[destCard]=newCards[i]; } newCards.CopyTo(Cars ,0); } } public enum Suit { Club,//梅花 Diamond,//方块 Heart,//红桃 Spade//黑桃 } public enum Rank { Ace=1,//幺点 Deuce,//两点 Three, Four, Fine, Six, Seven, Eight, Nine, Ten, Jack, Queen, King } }
数组实现集合的缺点:集合大小是固定的,不能改变。
3.使用System.Collections.ArrayList实现扑克牌集合
ArrayList也实现了IEnumerable、ICollection和IList接口,但比System.Array更复杂,和System.Array相比,它的大小是不固定的。

abstract class Animal { protected string name; public string Name { get { return name; } set { name = value; } } public Animal() { name = "The animal has no name."; } public Animal(string newName) { name = newName; } public void Feed() { Console.WriteLine("{0}has been fed.", name); } } class Cow : Animal { public void Milk() { Console.WriteLine("{0}has been milked.", name); } public Cow(string newName) : base(newName) { } } class Chicken : Animal { public void LagEgg() { Console.WriteLine("{0}has lag en egg.", name); } public Chicken(string newName) : base(newName) { } }

class Program { static void Main(string[] args) { 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 collection,Name= {1}",myAnimal.ToString(),myAnimal.Name); } Console.WriteLine("Array collection contains {0} objects", animalArray.Length); animalArray[0].Feed(); ((Chicken)animalArray[1]).LagEgg(); 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])).LagEgg(); animalArrayList.RemoveAt(0); animalArrayList.AddRange(animalArray);//后加入的排在前面 ((Cow)animalArrayList[0]).Milk(); 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[0]).Name); Console.ReadLine(); } }
数组和ArrayList的比较:
数组需要用固定大小的常量值来初始化,初始化后大小是固定的,ArrayList有两个构造函数,另一个构造函数可以设置列变的初始大小,如果集合的项个数超过了这个值就增加一倍。ArrayList的大小是变化的。
Animal[] animalArray = new Animal[2]; ArrayList animalArrayList = new ArrayList();
数组初始化之后,它的项并没有初始化,需要按照下标给每项分别初始化。ArrayList不能像数组那样初始化,因为他的项还没有添加,需要用Add方法才会添加一个新项,并为这个新项分配内存空间。
Cow myCow1 = new Cow("Deirdre"); animalArray[0] = myCow1; animalArray[1] = new Chicken("Ken"); Cow myCow2 = new Cow("Hayley"); animalArrayList.Add(myCow2); animalArrayList.Add(new Chicken("Roy"));
数组个数使用Length属性,ArrayList使用Count属性
数组是强类型集合,ArrayList是System.Object的集合,使用时需要把对象强制转换为对应的类型
animalArray[0].Feed(); ((Chicken)animalArray[1]).LagEgg(); ((Animal)animalArrayList[0]).Feed(); ((Chicken)(animalArrayList[1])).LagEgg();
ArrayList功能更多,比如RemoveAt,Remove,还可以使用AddRange方法把支持ICollection接口的对象加到集合中
4.使用CollectionBase建立集合
定制集合可以创建强类型化的集合,可以完全自己写代码创建集合,也可以通过继承现有类创建集合,一般通过继承集合底层的类创建集合。
CollectionBase类实现了IEnumerable、ICollection和IList接口,但是只提供了一些实现代码,比如IList的Clear()和RemoveAt,以及ICollection的Count属性,需要用到其他功能需要自己实现。

abstract class Animal { protected string name; public string Name { get { return name; } set { name = value; } } public Animal() { name = "The animal has no name."; } public Animal(string newName) { name = newName; } public void Feed() { Console.WriteLine("{0}has been fed.", name); } } class Animals : CollectionBase { public void Add(Animal newAnimal) { List.Add(newAnimal); } public void Remove(Animal oldAnimal) { List.Remove(oldAnimal); } /// <summary> /// 索引符只能访问已添加的项,超过Count-1会产生超出索引异常 /// </summary> /// <param name="animalIndex"></param> /// <returns></returns> public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; } set { List[animalIndex] = value; } } }
class Program { static void Main(string[] args) { Animals animalCollection = new Animals(); Cow myCow1 = new Cow("Deirdre"); animalCollection.Add(myCow1); animalCollection.Add(new Chicken("Ken")); foreach (Animal myAnimal in animalCollection) { Console.WriteLine("New {0} object added to collection,Name= {1}",myAnimal.ToString(),myAnimal.Name); } Console.WriteLine(animalCollection[1].Name); Console.ReadLine(); } }
5.使用DictionaryBase创建集合
DictionaryBase可以创建键值对集合,通过关键字访问项。DictionaryBase实现了IDictionary,IEnumerable、ICollection接口。和CollectionBase一样它提供了部分功能,如Clear()、和Count,但没有RemoveAt()因为它是IList接口上的功能,但是IDictionary接口有Remove功能,可以使用这个实现Remove功能。

abstract class Animal { protected string name; public string Name { get { return name; } set { name = value; } } public Animal() { name = "The animal has no name."; } public Animal(string newName) { name = newName; } public void Feed() { Console.WriteLine("{0}has been fed.", name); } } class Animals :DictionaryBase { public void Add(string newId, Animal newAnimal) { Dictionary.Add(newId, newAnimal); } public void Remove(string oldId,Animal oldAnimal) { Dictionary.Remove(oldId); } /// <summary> /// 索引符只能访问已添加的项,超过Count-1会产生超出索引异常 /// </summary> /// <param name="animalIndex"></param> /// <returns></returns> public Animal this[string key] { get { return (Animal)Dictionary[key]; } set { Dictionary[key] = value; } } } class Program { static void Main(string[] args) { Animals animalCollection = new Animals(); Cow myCow1 = new Cow("Deirdre"); animalCollection.Add("111",myCow1); animalCollection.Add("222",new Chicken("Ken")); foreach (DictionaryEntry d in animalCollection) { Console.WriteLine("New {0} object added to collection,Name= {1}",d.Value.ToString(),((Animal)d.Value).Name); } Console.WriteLine(animalCollection["111"].Name); Console.ReadLine(); } }
6.迭代器
我们知道IEnumerable接口是负责实现foreach循环的,假如循环的方式并不是我们想要的就需要重写循环方式,但是重写循环方式并不是很简单。我们看下在foreach循环中究竟干了什么。
在foreach循环中,迭代集合collectionObject的过程如下:
1)调用collectionObject.GetEnumerator(),返回一个IEnumerator引用。这个可以从IEnumerable接口的实现代码获取,但这是可选的,可以在实现IEnumerable接口的情况下创建一个GetEnumerator()用于循环(即下面说的迭代器);
2)调用IEnumerator的MoveNext方法;
3)如果MoveNext方法返回true,调用IEnumerator的Current获取对象的一个引用,用于foreach循环;
4)重复上面两步直到循环结束。
由此可以看出重写foreach循环方式需要重写几个方法(GetEnumerator(),MoveNext()等),跟踪索引,维护Current属性,以及执行其他一些操作,这需要做很多工作。比较简单的替代方法是使用迭代器。使用迭代器可以有效的在后台生成许多代码,不用我们去实现那么多。
迭代器是一个代码块,它按顺序提供了要在foreach循环中用到的值
class Program { static void Main(string[] args) { foreach(string s in SimpleList()) { Console.WriteLine(s); } Console.ReadLine(); } static IEnumerable SimpleList() { yield return "string 1"; yield return "string 2"; yield return "string 3"; } }
以上是一个迭代器的例子,在迭代器中使用yield关键字提供在foreach循环中用到的值。迭代器的返回类型有IEnumerable和IEnumerator两种类型,这两个类型使用的场合是:
1)如果要迭代一个类可以使用GetEnumerator方法,其返回类型是IEnumerator;
2) 如果要迭代一个类成员,比如一个方法,使用IEnumerable。
使用迭代器重写Animals foreach循环

abstract class Animal { protected string name; public string Name { get { return name; } set { name = value; } } public Animal() { name = "The animal has no name."; } public Animal(string newName) { name = newName; } public void Feed() { Console.WriteLine("{0}has been fed.", name); } } class Cow : Animal { public void Milk() { Console.WriteLine("{0}has been milked.", name); } public Cow(string newName) : base(newName) { } } class Chicken : Animal { public void LagEgg() { Console.WriteLine("{0}has lag en egg.", name); } public Chicken(string newName) : base(newName) { } } class Animals :DictionaryBase { public void Add(string newId, Animal newAnimal) { Dictionary.Add(newId, newAnimal); } public void Remove(string oldId,Animal oldAnimal) { Dictionary.Remove(oldId); } /// <summary> /// 索引符只能访问已添加的项,超过Count-1会产生超出索引异常 /// </summary> /// <param name="animalIndex"></param> /// <returns></returns> public Animal this[string key] { get { return (Animal)Dictionary[key]; } set { Dictionary[key] = value; } } public new IEnumerator GetEnumerator() { foreach (DictionaryEntry d in Dictionary) { yield return (Animal)d.Value; } } class Program { static void Main(string[] args) { Animals animals = new Animals(); animals.Add("111", new Cow("myCow1")); animals.Add("222", new Chicken("myChicken1")); foreach (Animal a in animals) { a.Feed(); } Console.ReadLine(); } }
属性块用做迭代器
class People:DictionaryBase { public void Add(string name, Person p) { this.Dictionary.Add(name, p); } public void Remove(string name) { this.Dictionary.Remove(name); } public Person this[string name] { get { return (Person)this.Dictionary[name]; } set { this.Dictionary[name] = value; } } public new IEnumerator GetEnumerator() { foreach (object d in Dictionary.Values) { yield return (Person)d; } } public IEnumerable Ages { get { foreach (object d in Dictionary.Values) { yield return ((Person)d).Age; } } } } public class Person:IComparable { public string Name { get; set; } public int Age { get; set; } [System.Xml.Serialization.XmlElementAttribute("Books")] public Books Books { get; set; } public Person(string name, int age) { Name = name; Age = age; } public int CompareTo(object obj) { return this.Age-((Person)obj).Age; } }