1 泛型结构
结构和类同属于复合类型,可以有字段成员、方法成员和嵌套成员。同样,可以通过引入类型参数来定义泛型结构。泛型类的定义规则,包括类型限制、继承定义、构造类型的指定等,同样可以应用于泛型结构。二者之间的本质区别仍在于引用类型和值类型之间的差别。
将MoreGenericSample程序进行修改,实现的功能类似。不同的是泛型类Relation<L,R>维护的只是一个数组,数组元素的类型则是泛型结构。
class GenericStructSample { static void Main(string[] args) { Relation<int, double> sinTable = new Relation<int, double>(180); for (int i = 0; i < 180; i++) { sinTable.SetLeft(i,i); sinTable.SetRight(i,Math.Sin(Math.PI * i / 180)); } //求正弦函数 Console.WriteLine("请输入度数(0~179之间):"); int x = int.Parse(Console.ReadLine()); double y = sinTable.GetRight(x); Console.WriteLine("Sin({0}度)={1}", x, y); //求反正弦函数 Console.WriteLine("请输入一个实数(0~1之间):"); double r = double.Parse(Console.ReadLine()); int pos; for (pos = 0; pos < sinTable.Length; pos++) { if (Math.Abs(r-sinTable.GetRight(pos)) < 0.005) { break; } } if (pos == sinTable.Length) Console.WriteLine("没有找到指定的反正弦值"); else Console.WriteLine("arcsin({0})={1}度", r, sinTable.GetLeft(pos)); } } /// <summary> /// 泛型结构:关联元素RelationElement /// </summary> public struct RelationElement<L,R> { //字段 private L m_left; private R m_right; //属性 public L Left { get { return m_left; } set { m_left = value; } } public R Right { get { return m_right; } set { m_right = value; } } //构造函数 public RelationElement(L left, R right) { m_left = left; m_right = right; } } /// <summary> /// 泛型类:关联Relation /// </summary> public class Relation<L,R> { //字段 public RelationElement<L, R>[] m_list; //属性 public int Length { get { return m_list.Length; } } //构造函数 public Relation(int iLength) { m_list = new RelationElement<L, R>[iLength]; } //方法 public L GetLeft(int index) { return m_list[index].Left; } public R GetRight(int index) { return m_list[index].Right; } public void SetLeft(int index,L left) { m_list[index].Left=left; } public void SetRight(int index,R right) { m_list[index].Right = right; } public int FindLeft(L left) { for (int i = 0; i < Length; i++) { if (m_list[i].Equals(left)) return i; } return -1; } public int FindRight(R right) { for (int i = 0; i < Length; i++) { if (m_list[i].Equals(right)) return i; } return -1; } }
2 泛型接口
2.1 泛型接口的定义
泛型接口是比普通接口更为抽象的数据类型。和泛型类一样,可以为泛型接口指定一个或多个类型参数,也可以为类型参数添加类型限制。而泛型接口本身也可用于类型限制。例如:
public interface IOutput<T>{} public interface IAssemble<T> where T:IComparable,IOutput<T>{}
和泛型类类似,在类型限制的定义中,要求用于限制的接口要么是封闭类型,要么所含的类型参数在所定义的泛型接口的类型参数列表中出现。例如下面的代码是不合法的:
public interface IAssemble<T> where T : IOutput<U>{}//U未在IAssemble的定义中出现 public interface IAssemble<T> where T :IComparable,IOutput{}//IOutput类型不完整 public class Assemble<T> where T:IRelation<S,T>{}//S未在Assemble的定义中出现
接口是抽象数据类型,它只可以有方法成员,而且只定义方法的标识而不提供方法的实现。
泛型接口所定义的类型参数可以在其方法成员中作为参数或返回类型来使用。作为方法参数使用时,还可以是引用参数、输出参数和数组参数,例如:
public interface IRelation<L,R> { int FindLeft(L left); int FindRight(R right); L GetLeft(int index); void GetRight(int index,out R right); void Change(ref L left,ref R right); }
2.2 唯一性规则
接口之间、类和接口之间可以是多继承的关系。引入了泛型接口之后问题就变得更为复杂。类可能是普通类,也可能是泛型类;泛型类可能是开放类型,也可能是封闭类型。接口也存在同样的情况,可能是普通接口,也可能是泛型接口;泛型接口可能是开放类型,也可能是封闭类型。但根本的原则没有变:开放类型不能从封闭类型中继承。当参与继承的双方都是泛型时,要求被继承的类型中的类型参数在派生类型的定义中出现。
如果多个接口定义了相同的方法成员,其派生类既可以用一个成员方法来实现,也可以通过多个带接口声明的方法加以区分。例如:
public interface IA<S> { void F(); } public interface IB<T> { void F(); } public interface IRelation<L,R> : IA<L>,IB<R> { new void F(); } public class IndexedAssemble<T> : IRelation<int,T> { void IA<int>.F() { // } void IB<T>.F() { // } void IRelation<int,T>.F() { // } }
对于某个泛型接口,如果某个类或接口同时继承该泛型接口的多个构造类型,就可能导致同名的方法成员。例如下面的代码是错误的:
public class C<S,T> : IA<S>,IA<T>{}
因为将来为泛型类C指定构造类型时,类型参数S和T可能被替换为同一种类型。例如C<int,int>,其中就会出现两个名为IA<int>.F的方法。
下面的定义就是允许的:
public class C<T> : A<int,T>,B<T,string>{}
2.3 泛型接口与继承
和泛型类一样,引入泛型接口的目的之一是为了在保留继承优越性的同时,避免频繁的类型转换。在很多情况下,只提供泛型类并不能完全解决这些问题。
下面是一个完整的例子:
class GenericInterfaceSample { static void Main() { IndexedAssemble<Contact> conAsm1 = new IndexedAssemble<Contact>(5); conAsm1.Right = new Business[5] { new Business("Mike Even"), new Business("李明"), new Business("David Gries"), new Business("张鹏"), new Business("White Brown") }; IndexedAssemble<Contact> conAsm2 = new IndexedAssemble<Contact>(3); conAsm2.Right = new ClassMate[3] { new ClassMate("李明"), new ClassMate("Frank Douf"), new ClassMate("何子杰") }; IndexedAssemble<Contact> conAsm = (IndexedAssemble<Contact>)conAsm1.Merge(conAsm2); //conAsm.Output(); conAsm.Sort(); conAsm.Output(); } } /// <summary> /// 泛型接口:可合并IMerge /// </summary> public interface IMerge<T> { int Length { get; } T this[int index] { get; set; } IMerge<T> Merge(T tp); IMerge<T> Merge(IMerge<T> others); } /// <summary> /// 泛型类:关联Relation /// </summary> public class Relation<L,R> { //字段 public L[] Left; public R[] Right; //属性 public int Length { get { return Left.Length; } } //构造函数 public Relation(int iLength) { Left = new L[iLength]; Right = new R[iLength]; } //方法 public int FindLeft(L left) { for (int i = 0; i < Length; i++) { if (Left[i].Equals(left)) return i; } return -1; } public int FindRight(R right) { for (int i = 0; i < Length; i++) { if (Right[i].Equals(right)) return i; } return -1; } } /// <summary> /// 泛型类:索引集合 /// </summary> public class IndexedAssemble<T> : Relation<int,T>,IMerge<T> where T:IOutput,IComparable { //索引函数 public T this[int index] { get { return Right[index]; } set { Right[index] = value; } } //构造函数 public IndexedAssemble(int iLength) : base(iLength) { for (int i = 0; i < Length; i++) Left[i] = i; } //方法 public void Sort() { T tmp; for (int i=Length-1;i>0;i--) { for(int j=0;j<i;j++) { if(Right[Left[j]].CompareTo(Right[Left[j+1]])>0) { tmp=Right[j+1]; Right[j+1]=Right[j]; Right[j]=tmp; } } } } public void Output() { for (int i = 0; i < Length; i++) Right[Left[i]].Output(); } public IMerge<T> Merge(T tp) { IndexedAssemble<T> result = new IndexedAssemble<T>(Length + 1); for(int i=0;i<Length;i++) result[i]=Right[i]; result[Length]=tp; return result; } public IMerge<T> Merge(IMerge<T> others) { IndexedAssemble<T> result = new IndexedAssemble<T>(Length + others.Length); for (int i = 0; i < this.Length; i++) result[i] = Right[i]; for (int i = 0; i < others.Length; i++) result[this.Length + i] = others[i]; return result; } }
程序将合并之后的联系人按集合按Name属性的字母顺序输出:
商务:David Gries先生/女士 住宅电话:未知 办公电话:未知 手机:未知 商务传真:未知 同学:Frank Douf 住宅电话:未知 办公电话:未知 手机:未知 生日:0001-1-1 0:00:00 商务:Mike Even先生/女士 住宅电话:未知 办公电话:未知 手机:未知 商务传真:未知 商务:White Brown先生/女士 住宅电话:未知 办公电话:未知 手机:未知 商务传真:未知 同学:何子杰 住宅电话:未知 办公电话:未知 手机:未知 生日:0001-1-1 0:00:00 商务:李明先生/女士 住宅电话:未知 办公电话:未知 手机:未知 商务传真:未知 同学:李明 住宅电话:未知 办公电话:未知 手机:未知 生日:0001-1-1 0:00:00 商务:张鹏先生/女士 住宅电话:未知 办公电话:未知 手机:未知 商务传真:未知 请按任意键继续. . .
2.4 集合中的泛型
第七章第3节中介绍过.NET类库中与集合相关的若干接口,不过他们的管理目标都是object类型。在引入了泛型接口后,就可以按特定的集合元素类型进集合进行管理了。
接口 | 支持的概念 | 继承的接口 | 主要方法 |
IEnumerator<T> | 枚举器 | 无 | bool MoveNext() |
IEnumerable<T> | 可列举的集合 | 无 | IEnumerator<T>GetEnumerator() |
ICollection<T> | 有长度集合 | IEnumerable<T> | void CopyTo(T[],int) |
void Add(T) | |||
bool Remove(T) | |||
void Clear() | |||
IList<T> | 可索引的对象列表 | ICollection<T>,IEnumerable<T> | void Insert(int,T) |
int IndexOf(T) | |||
void RemoveAt(int) | |||
IComparable<T> | 可比较的对象 | 无 | int CompareTo(T) |
bool Equals(T) | |||
IComparer<T> | 比较器 | 无 | int CompareTo(T,T) |
bool Equals<T,T> | |||
int GetHashCode<T> | |||
IDictionary<K,V> | 有字典序的集合 |
ICollection<KeyValuePair<K,V>>, IEnumerable<KeyValuePair<K,V>> |
int Add(K,V) |
bool Contains(K) | |||
bool Remove(K) |
在第七章中,Contact类继承了IComparable接口,并实现了该方法,用于比较两个联系人对象姓名是否相等。
方法中不可避免地涉及到了类型转换。如果改用泛型接口,使Contact类继承IComparable<Contact>接口就能避免这一问题。下面的程序实现了这一功能:
class GenericCollectionSample { static void Main() { Contact c1 = new Business("Mike Even"); Contact c2 = new Business("李明"); Contact c3 = new ClassMate("David Gries"); Contact c4 = new ClassMate("李明"); Console.WriteLine("Mike Even(Business) < 李明(Business)?:{0}", c1.CompareTo(c2) < 0); Console.WriteLine("李明(Business)==李明(Classmate)?:{0}", c2 == c4); Console.WriteLine("李明(Business) Equals 李明(Classmate)?:{0}", c2.Equals(c4)); } } /// <summary> /// 基类:联系人Contact /// </summary> public class Contact:IOutput,IComparable { //字段 protected string m_name; protected string m_homePhone = "未知"; protected string m_busiPhone = "未知"; protected string m_mobilePhone = "未知"; //属性 public string Name { get { return m_name; } set { m_name = value; } } //虚拟索引函数 public virtual string this[string sType] { get { string type = sType.ToUpper(); switch (type) { case "住宅电话": return m_homePhone; case "办公电话": return m_busiPhone; case "手机": return m_mobilePhone; default: return null; } } set { string type = sType.ToUpper(); switch (type) { case "住宅电话": m_homePhone = value; break; case "办公电话": m_busiPhone = value; break; case "手机": m_mobilePhone = value; break; default: throw new ArgumentOutOfRangeException(); } } } //构造函数 public Contact(string sName) { m_name = sName; } //虚拟方法 public virtual void Output() { Console.WriteLine("姓名:{0}", m_name); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine(); } //方法 //public int CompareTo(Contact con) //{ // return this.m_name.CompareTo(con.m_name); //} public int CompareTo(object obj) { if (obj is Contact) return this.m_name.CompareTo(((Contact)obj).m_name); return -1; } public bool Equals(Contact con) { return this.Name.Equals(con.Name); } } /// <summary> /// 派生类:商务Business /// </summary> public class Business:Contact,IOutput { //字段 protected string m_busiFax = "未知"; protected string m_title = "先生/女士"; //属性 public string Title { get { return m_title; } set { m_title = value; } } //重载索引函数 public override string this[string sType] { get { string type=sType.ToUpper(); switch(type) { case "商务传真": return m_busiFax; default: return base[sType]; } } set { string type = sType.ToUpper(); switch (type) { case "商务传真": m_busiFax = value; break; default: base[sType] = value; break; } } } //构造函数 public Business(string sName) : base(sName) { } //重载方法 public override void Output() { Console.WriteLine("商务:{0}", m_name+m_title); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine("商务传真:{0}", m_busiFax); Console.WriteLine(); } //接口方法 void IOutput.Output() { Console.WriteLine("商务:{0}", m_name + m_title); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine("商务传真:{0}", m_busiFax); Console.WriteLine(); } } /// <summary> /// 派生类:同学ClassMate /// </summary> public class ClassMate:Contact { //字段 protected DateTime m_birthday; //属性 public DateTime Birthday { get { return m_birthday; } set { m_birthday = value; } } //构造函数 public ClassMate(string sName) : base(sName) { } //重载方法 public override void Output() { Console.WriteLine("同学:{0}", m_name); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine("生日:{0}", m_birthday.ToString()); Console.WriteLine(); } }
注意,Contact类在继承泛型接口IComparable<T>(包括其构造类型)时,需要同时实现该接口的两个方法CompareTo和Equals。程序主方法中最后两行,分别使用了相等操作符和Equals方法来比较两个名为“李明”的对象,第一次比较的是二者是否指向同一个引用,而第二次则是调用Contact类的Equals方法来比较二者姓名是否相等。
程序输出结果:
Mike Even(Business) < 李明(Business)?:True Mike Even(Business) < David Gries(Classmate) ? : False 李明(Business)==李明(Classmate)?:False 李明(Business) Equals 李明(Classmate)?:True 请按任意键继续. . .
将两个联系人集合合并的这程中可能会出现重复的对象。下面程序进行了修改,在出现重复时将只保留其中一个对象,而忽略掉其它多余的对象。
class GenericImplementSample { static void Main() { IndexedAssemble<Contact> conAsm1 = new IndexedAssemble<Contact>(5); conAsm1.Right = new Business[5] { new Business("Mike Even"), new Business("李明"), new Business("David Gries"), new Business("张鹏"), new Business("White Brown") }; IndexedAssemble<Contact> conAsm2 = new IndexedAssemble<Contact>(3); conAsm2.Right = new ClassMate[3] { new ClassMate("李明"), new ClassMate("Frank Douf"), new ClassMate("何子杰") }; IndexedAssemble<Contact> conAsm = (IndexedAssemble<Contact>)conAsm1.Merge(conAsm2); //conAsm.Output(); conAsm.Sort(); conAsm.Output(); } } /// <summary> /// 泛型类:关联Relation /// </summary> public class Relation<L,R> where R:IComparable { //字段 public L[] Left; public R[] Right; //属性 public int Length { get { return Left.Length; } } //构造函数 public Relation(int iLength) { Left = new L[iLength]; Right = new R[iLength]; } //方法 public int FindLeft(L left) { for (int i = 0; i < Length; i++) { if (Left[i].Equals(left)) return i; } return -1; } public int FindRight(R right) { for (int i = 0; i < Length; i++) { if (Right[i].Equals(right)) return i; } return -1; } } /// <summary> /// 泛型类:索引集合 /// </summary> public class IndexedAssemble<T> : Relation<int, T>, IMerge<T> where T : IOutput, IComparable { //索引函数 public T this[int index] { get { return Right[index]; } set { Right[index] = value; } } //构造函数 public IndexedAssemble(int iLength) : base(iLength) { for (int i = 0; i < Length; i++) Left[i] = i; } //方法 public void Sort() { T tmp; for (int i=Length-1;i>0;i--) { for(int j=0;j<i;j++) { if(Right[Left[j]].CompareTo(Right[Left[j+1]])>0) { tmp=Right[j+1]; Right[j+1]=Right[j]; Right[j]=tmp; } } } } public void Output() { for (int i = 0; i < Length; i++) Right[Left[i]].Output(); } public IMerge<T> Merge(T tp) { if(this.FindRight(tp)!=-1) return this; IndexedAssemble<T> result = new IndexedAssemble<T>(Length + 1); for(int i=0;i<Length;i++) result[i]=Right[i]; result[Length]=tp; return result; } public IMerge<T> Merge(IMerge<T> others) { IMerge<T> result = this; for (int i = 0; i < others.Length; i++) result = result.Merge(others[i]); return result; } }
3 小结
C#把泛型的优越性带到了整个类型系统当中,其中当然少不了泛型结构和泛型接口。泛型结构和泛型类非常类似,但它通常出现在需要值类型的场合。
和普通接口一样,泛型接口是只有定义而不提供实现,但在接口继承特别是多继承的时候,要充分考虑泛型接口及其构造类型的区别,保持继承定义和方法实现的惟一性。
泛型类和泛型接口能够共同实现算法与数据结构的抽象和分离。