第九章 泛型结构和接口
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("沒有找到指定的反正弦值");elseConsole.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;}} View Code2 泛型接口
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;}} View Code程序將合并之后的聯系人按集合按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();}} View Code注意,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;}} View Code?3 小結
C#把泛型的優越性帶到了整個類型系統當中,其中當然少不了泛型結構和泛型接口。泛型結構和泛型類非常類似,但它通常出現在需要值類型的場合。
和普通接口一樣,泛型接口是只有定義而不提供實現,但在接口繼承特別是多繼承的時候,要充分考慮泛型接口及其構造類型的區別,保持繼承定義和方法實現的惟一性。
泛型類和泛型接口能夠共同實現算法與數據結構的抽象和分離。
轉載于:https://www.cnblogs.com/boywg/p/4134522.html
總結
以上是生活随笔為你收集整理的第九章 泛型结构和接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统小文-转载
- 下一篇: 使用FastCoder写缓存单例