目录
一、集合
集合类Collection Class
C#中的数组实现为System.Array类的实例,只是集合类中的一种类型
集合类一般用于处理对象列表,功能大多是通过实现System.Collections名称空间中的接口而获得
集合的功能(包括基本功能)可以通过接口来实现,可以使用基本集合类(System.Array),可以创建定制集合类
System.Collections名称空间中的几个接口提供了基本的集合功能:
IEnumerable可以迭代集合中的项
ICollection(继承于IEnumerable)可以获取集合中项的个数,并能把项复制到一个简单的数组类型中
IList(继承于IEnumerable和ICollection)提供了集合的项列表,允许访问这些项,并提供其他一些与项列表相关的基本功能
IDictionary(继承于IEnumerable和ICollection)类似于IList,但提供了可通过键值(而不是索引)访问的项列表
System.Array类实现了IList、ICollection和IEnumerable,但不支持IList的一些更高级功能,它表示大小固定的项列表
二、使用集合
Systems.Collections名称空间中的类System.Collections.ArrayList也实现了IList、ICollection和IEnumerable接口,但实现方式比System.Array更复杂。
System.Array数组的大小是固定不变的(不能添加或删除元素)
System.Collections.ArrayList类可以用于表示大小可变的项列表
代码参考:
https://media.wiley.com/product_ancillary/85/11190966/DOWNLOAD/Chapter11code.zip
准备好的基类和派生类先写在这里,使用的命名空间都是Animals
基类 Animal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
/// <summary>
///
/// </summary>
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() => WriteLine($"{name} has been fed.");
}
}
派生类 Cow.cs Chicken.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
public class Cow : Animal
{
public void Milk() => WriteLine($"{name} has been milked.");
public Cow(string newName) : base(newName) { }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
public class Chicken : Animal
{
public void LayEgg() => WriteLine($"{name} has laid an egg.");
public Chicken(string newName) : base(newName) { }
}
}
第一个集合使用System.Array类(这是一个简单数组),Animal对象
//Animal类是抽象类,不能实例化,但通过多态性,使集合中的项成为派生于Animal类的Cow和Chicken类实例
//对于简单数组,用固定的大小来初始化数组
Animal[] animalArray = new Animal[2];
//数组是引用类型(如Animal和Animal派生的对象),用长度初始化数据,但并没有初始化它包含的项;
//要使用一个指定的项还需初始化,赋予初始化完成的对象
//用现有的Cow对象来赋值,或者通过创建一个新的Chicken对象来赋值
Cow myCow1 = new Cow("Lea");
animalArray[0] = myCow1;
animalArray[1] = new Chicken("Noa");
//Systen.Array类实现IEnumerable接口,这个接口的唯一方法GetEnumerator()可以迭代集合中的各项
foreach (Animal myAnimal in animalArray)
{
WriteLine($"New {myAnimal.ToString()} object "
+ $"added to Array collection, Name = {myAnimal.Name}");
}
//使用数组的Length属性,输出数组中元素的个数
WriteLine($"Array collection contains {animalArray.Length} objects.");
//简单数组是强类型化的,可以直接访问所包含的项类型,直接调用项的方法
animalArray[0].Feed();
//数组的类型时抽象类型Animal,不能直接调用由派生类提供的方法,而必须使用数据类型转换
((Chicken)animalArray[1]).LayEgg();
WriteLine();
WriteLine("Create an ArrayList type collection of Animal " +
"objects and use it:");
第二个集合使用System.Collections.ArrayList类,Animal对象
//Animal类是抽象类,不能实例化,但通过多态性,使集合中的项成为派生于Animal类的Cow和Chicken类实例
//ArrayList集合不需要初始化其大小,创建animalArrayList列表:这个类还有另外两种构造函数,
//1、构造函数把现有的集合作为一个参数,将其内容复制到新实例中;
//2、而另一个构造函数通过一个参数设置集合的容量(capacity)。
//这个容量用一个int值指定,设置集合中可以包含的初始项数。但这并不是绝对容量,因为若集合中的项数超过该值,容量会自动增加一倍
ArrayList animalArrayList = new ArrayList();
//对于ArrayList集合,没有现成的项,也没有null引用的项。
//使用ArrayList对象的Add()方法添加新项
Cow myCow2 = new Cow("Rual");
animalArrayList.Add(myCow2);
animalArrayList.Add(new Chicken("Andrea"));
//ArrayList对象也支持IEnumerable接口,可以与foreach一起使用
foreach (Animal myAnimal in animalArrayList)
{
WriteLine($"New {myAnimal.ToString()} object added to ArrayList collection," +
$"Name = {myAnimal.Name}");
}
//使用Count属性,输出ArrayList对象中元素的个数,该属性是ICollection接口的一部分
WriteLine($"ArrayList collection contains {animalArrayList.Count} objects.");
//ArrayList集合是System.Object对象的集合(通过多态性赋给Animal对象),所以必须对所有的项进行数据类型转换
((Animal)animalArrayList[0]).Feed();
((Chicken)animalArrayList[1]).LayEgg();
WriteLine();
WriteLine("Additional manipulation of ArrayList:");
//利用的一些ArrayList集合功能超出了Array集合的功能范围
//可以使用Remove()和RemoveAt()方法删除项,这两个方法是在ArrayList类中实现的IList接口的一部分,
//他们分别根据项的引用或索引从数组中删除项
//删除列表中的第一项,即Name属性为Rual的Cow对象
//也可以使用animalArrayList.Add(myCow2);效果相同
animalArrayList.RemoveAt(0);
//集合中剩余的项是Chicken对象
//当对ArrayList对象中的项进行修改,使数组中剩下N个项时,其索引范围变为0~N-1,
//例如:删除索引为0的项,会使其他项在数组中移动一个位置,所以应使用索引0(而非1)来访问Chicken对象。
//不再有索引为1的项了(因为集合中最初只有两个项),所以使用((Animal)animalArrayList[1]).Feed();,就会抛出异常
((Animal)animalArrayList[0]).Feed();
//ArrayList集合可用AddRange()方法一次添加多项,该方法接受带ICollection接口的任意对象,包括前面代码创建的animalArray数组
//AddRange()方法不是ArrayList提供的任何接口的一部分,这个方法专用于ArrayList类
//还可以使用InsertRange(),可以把数组对象插入到列表中的任何位置
animalArrayList.AddRange(animalArray);
((Chicken)animalArrayList[2]).LayEgg();
//使用IList接口中的IndexOf()方法可以看出myCow1(最初添加到animalArray中的一个对象)
//现在是animalArrayList集合的一部分
Console.WriteLine($"The animal called {myCow1.Name} is at " +
$"index {animalArrayList.IndexOf(myCow1)}.");
//通过对象引用重新命名了对象
myCow1.Name = "Mary";
WriteLine("The animal is now " +
$"called {((Animal)animalArrayList[1]).Name}.");
ReadKey();
三、定义集合
创建自己的强类型化的集合
从一个类中派生出自己的集合,例如System.Collections.CollectionBase类,这个抽象类提供大量实现代码
CollectionBase类有接口IEnumerable、ICollection和IList,主要是IList的Clear()和RemoveAt()方法,以及ICollection的Count属性
CollectionBase提供了两个受保护的属性,它们可以访问存储的对象本身,可以使用List和InnerList,List可以通过IList接口访问项,InnerList则是用于存储项的ArrayList对象
存储Animal对象的集合类
代码中的Add()和Remove()方法实现为强类型化的方法,使用IList接口中用于访问项的标准Add()方法。这些方法现在只用于处理Animal类或派生于Animal的类,而前面介绍的ArrayList实现代码可以处理任何对象
public class Animals:CollectionBase
{
public void Add(Animal newAnimal)
{
List.Add(newAnimal);
}
public void Remove(Animal oldAnimal)
{
List.Remove(oldAnimal);
}
public Animals()
{
}
}
CollectionBase类可以对派生的集合使用foreach语法
Console.WriteLine("Using custom collection class Animals:");
Animals animalCollection = new Animals();
animalCollection.Add(new Cow("Sarah"));
foreach(Animal myAnimal in animalCollection)
{
Console.WriteLine("New {0} object added to custom collection, " +
"Name = {1}", myAnimal.ToString(), myAnimal.Name);
}
但不能使用以下的代码:
animalCollection[0].Feed();
若要以这种方式,需通过索引来访问项,就需要使用索引符
四、索引符
索引符(indexer)是一种特殊类型的属性,可以添加到一个类中,以提供类似于数组的访问
常见用法是对项实现简单的数字索引
//在Animal对象的Animals集合中添加一个索引符
//this关键字需要与方括号中的参数一起使用
public class Animals:CollectionBase
{
...
public Animals this[int animalIndex]
{
get
{
//这段代码对List属性使用一个索引符(即在IList接口上,
//可以访问CollectionBase中的ArrayList,ArrayList存储了项)
return (Animal)List[animalIndex];
}
set
{
//需要显式数据类型转换,因为IList.List属性返回一个System.Object对象
List[animalIndex] = value;
}
}
}
这种强类型化功能意味着可使用代码:
animalCollection[0].Feed();
而不是
((Animal)animalCollection[0]).Feed();
代码参考:
https://media.wiley.com/product_ancillary/85/11190966/DOWNLOAD/Chapter11code.zip
修改 Program.cs的程序
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
class Program
{
static void Main(string[] args)
{
//集合使用自己创建的强类型化的集合
AnimalList animalList = new AnimalList();
Cow myCow2 = new Cow("Rual");
animalList.Add(myCow2);
animalList.Add(new Chicken("Andrea"));
foreach (Animal myAnimal in animalList)
{
WriteLine($"New {myAnimal.ToString()} object added to ArrayList collection," +
$" Name = {myAnimal.Name}");
}
WriteLine($"ArrayList collection contains {animalList.Count} "
+ " objects.");
animalList[0].Feed();
((Chicken)animalList[1]).LayEgg();
WriteLine();
}
}
}
五、使用List<T>泛型集合类
List<T>泛型集合类更加快捷和易于使用,不必和从CollectionBase中派生一个类(前文的方法)
使用List<T>泛型集合类进行替换上述自己的强类型化的集合Animals,可删除Animals.cs文件,无需声明Animals类
使用命名空间System.Collections.Generic
Programm.cs修改为
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
class Program
{
static void Main(string[] args)
{
//使用System.Collections.Generic命名空间中的泛型集合类
List<Animal> animalList = new List<Animal>();
Cow myCow2 = new Cow("Rual");
animalList.Add(myCow2);
animalList.Add(new Chicken("Andrea"));
foreach (Animal myAnimal in animalList)
{
WriteLine($"New {myAnimal.ToString()} object added to ArrayList collection," +
$" Name = {myAnimal.Name}");
}
WriteLine($"ArrayList collection contains {animalList.Count} "
+ " objects.");
animalList[0].Feed();
((Chicken)animalList[1]).LayEgg();
WriteLine();
}
}
}
六、键控集合和IDictionary
除了IList接口外,集合还可以实现类似IDictionary接口,允许项通过键值(如字符串名)进行索引,而不是通过一个索引。可使用索引符完成,但这次的索引符参数是与存储的项相关联的键,而不是int索引
使用DictionaryBase基类简化IDictionary接口的实现,基类实现IEnumerable和ICollection,提供对任何集合都相同的基本集合处理功能
DictionaryBase与CollectionBase一样,实现通过其支持的接口获得的一些成员(但不是全部成员)。DictionaryBase也实现Clear和Count成员,但不实现RemoveAt()。
RemoveAt()是IList接口中的一个方法,不是IDictionary接口中的一个方法。
IDictionary有一个Remove()方法,这是基于DictionaryBase的定制集合类上实现的方法。
Animals类的另一个版本,该类派生于DictionaryBase,包括Add()、Remove()和一个通过键访问的索引符的实现代码:
public class Animals:DictionaryBase
{
//Add()带有两个参数:一个键和一个值,存储在一起
//字典集合有一个继承于DictionaryBase的成员Dictionary,这个成员是一个IDictionary接口,
//有自己的Add()方法,该方法带有两个object参数
//实现代码使用一个string值作为键,使用一个Animal对象作为与该键存储在一起的数据
public void Add(string newID, Animal newAnimal)
{
Dictionary.Add(newID, newAnimal);
}
//以一个键而不是对象引用作为参数,与指定键值对应的项被删除
public void Remove(string animalID)
{
Dictionary.Remove(animalID);
}
public Animals()
{
}
//使用一个字符串键值,而不是一个索引,用于通过Dictionary的继承成员来访问存储的项,仍需数据类型转换
public Animal this[string animalID]
{
get
{
return (Animal)Dictionary[animalID];
}
set
{
Dictionary[animalID] = value;
}
}
}
七、使用Dictionary<K,V>泛型集合类
这个类型可以定义键/值对的集合,该类需要实例化两个类型,分别用于键和值,以表达集合中的各个项
使用该类和List<T>使用情况大致相同,无需再声明Animals类,索引符可使用string类或枚举
使用命名空间System.Collections.Generic
Programm.cs修改为
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Animals
{
class Program
{
static void Main(string[] args)
{
//使用System.Collections.Generic命名空间中的泛型集合类
//使用Dictionary<K,V>泛型集合类
Dictionary<string,Animal> animalDict = new Dictionary<string,Animal>();
Cow myCow2 = new Cow("Rual");
animalDict.Add("Cow",myCow2);
animalDict.Add("Chicken",new Chicken("Andrea"));
foreach (string key in animalDict.Keys)
{
WriteLine(key);
}
foreach (Animal value in animalDict.Values)
{
WriteLine(value.Name);
}
animalDict["Cow"].Feed();
((Chicken)animalDict["Chicken"]).LayEgg();
WriteLine();
}
}
}