(10)泛型

来源:互联网 发布:如何安装pyqt4 linux 编辑:程序博客网 时间:2024/05/16 12:16
 

(10)泛型

分类: C# 39人阅读 评论(0) 收藏 举报

*泛型类型概述

参数化类型:

*可空类型也是泛型。

[csharp] view plaincopy
  1. //System.Nullable<int> nullableInt;  
  2. int? op1 = 5;  
  3. //int result = op1 * 5;无法将int?隐式转换为int,需要显示转换。  
  4. //如果op1是null,就会生成System.InvalidOperationException类型的异常。  
  5. int result = (int)op1 * 5;  
  6. Console.Write(result);  
  7. Console.ReadKey();  
  8. //除了bool?之外的简单可控类型,该造作的结果都是null.对于bool?为& 和|操作会得到非空值。  
  9. //op1    op2     &      |  
  10. //true   null    null   true  
  11. //false  null    false  null  
  12. // null  null    null   null   

利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构。使特定泛型类型的每个变量都有相同的内部算法,但数据类型和方法签名可以不同。

用<>来表示要处理的数据类型。

*System.Collection.Generic名称空间中包含的类型有:

List<T>  :T类型对象的集合

Dictionary<K,V>   :与K类型的键值相关的V类型的项的集合

*泛型接口和struct

[csharp] view plaincopy
  1. //泛型接口  
  2. interface Ipair<T>  
  3. {  
  4.     T first { setget; }  
  5.     T second { setget; }  
  6. }  
  7.   
  8. //实现一个泛型接口  
  9. public struct Pair<T> : Ipair<T>  
  10. {  
  11.     public T first  
  12.     {  
  13.         get  
  14.         {  
  15.             return _First;  
  16.         }  
  17.         set   
  18.         {  
  19.             _First = value;   
  20.         }  
  21.     }  
  22.     private T _First;  
  23.   
  24.     public T second  
  25.     {  
  26.         get  
  27.         {  
  28.             return _Second;  
  29.         }  
  30.         set  
  31.         {  
  32.             _Second = value;  
  33.         }  
  34.     }  
  35.     private T _Second;  
  36. }  

public struct Pair<T>:Ipair<T>{}

**List<T>
List<T> myCollection =new List<T>;//创建T类型的对象,没有定义类,实现方法和进行其他操作。

对象有下属方法和属性:

int Count ;

void  Add( T item);

void AddRange(IEnumerable<T>);//把多个项添加到集合中去。

IList<T>AsReadOnly();//给集合返回一个只读接口。

int Capacity ;//获取或设置集合可以包含的项数。

void Clear();

bool Contains(T item);//确定item是否包含在集合中。

void CopyTo(T[] array ,int index)

int IndexOf(T item) ;//获取item的索引,如果项没有包含在集合上,就返回-1;

void Insert (int index ,T item);

bool Remove();

void RemoveAt();//从集合中删除索引index处的项

[csharp] view plaincopy
  1. //有Animal,Cow和Chicken类,其中Animal是基类,有Feed()方法  
  2. List<Animal> animalCollection = new List<Animal>();  
  3. animalCollection.Add(new Cow("Jack"));  
  4. animalCollection.Add(new Chicken("Vera"));  
  5. foreach (Animal myAnimal in animalCollection)  
  6. {  
  7.     myAnimal.Feed();  
  8. }  
**对泛型列表进行排序和搜索【不熟悉】

使用泛型接口IComparer<T>和IComparable<T>

int IComparable<T>      CompareTo(T otherObj)

             bool IComparable<T>   Equals(T otherObj)

             int ICompare<T>             Compare(TobjectA,TobjectB)

     bool IComparer<T>        Equals(TobjectA,TobjectB)

     int IComparer<T>            GetHashCode(T objectA)

要对List<T>排序,可以在要排序的类型上提供IComparable<T>接口,或者提供IComparer<T>接口。

一般情况下,给列表排序需要有一个方法来比较T类型的两个对象,要在列表中搜索,也要有一个方法类检查T对象,看看它是否满足某个条件。

解决以上的问题可以使用两个泛型委托  Comparison<T>:这个委托用于排序方法,其返回值类型和参数是 int method(T objectA,T objectB)

Predicate<T>:这个方法用于搜索,其返回值类型和参数是bool method(T targetObject)

*定义泛型类 :其中T是任意标识符,但一般使用T。可以在定义中包含多个类型。

*类型参数命名的指导原则:使用T作为前缀。

class MyGenericClass<T>

{//...

}

class MyGenericClass<T1,T2,T3>

{//...

}

我们不知道T1是什么,也就不能使用它的构造函数,它甚至可能没有可公共访问的默认构造函数。可以对T1进行如下的假设:T1继承自System.Object的类型或封箱到System.Object中。T1的实例不能进行有趣的操作,一般可以typeof(T1).ToString();另一个限制是,T只能用!= ,== 来运算。

[csharp] view plaincopy
  1. class MyGenericClass<T1, T2, T3>  
  2. {   //T1,T2,T3可以用作变量的类型,属性或方法等成员的返回类型。  
  3.     private T1 innerT1Object;  
  4.   
  5.     public MyGenericClass(T1 item)  
  6.     {  
  7.         innerT1Object = item;  
  8.     }  
  9.   
  10.     public T1 InnerT1Object  
  11.     {  
  12.         get  
  13.         {  
  14.             return innerT1Object;  
  15.         }  
  16.     }  
  17. }  
[csharp] view plaincopy
  1. public bool Compare(T1 op1, T2 op2)  
  2. {  
  3.     if (op1 != null && op2 != null)  
  4.     {  
  5.         return true;  
  6.     }  
  7.     else  
  8.     {  
  9.         return false;  
  10.     }  
  11. }  


*在一个类中重复一个接口实现

[csharp] view plaincopy
  1. public interface IContainer<T>  
  2. {  
  3.     ICollection<T> Items  
  4.     {  
  5.         set;  
  6.         get;  
  7.     }  
  8. }  
  9.   
  10. public class Person : IContainer<Adress>, IContainer<Phone>, IContainer<Emiall>  
  11. {  
  12.     IContainer<Adress> IContainer<Adress>.Items  //Items属性使用一个显示接口实现多次出现,每一次参数类型都有所不同。  
  13.     {  
  14.         set{};  
  15.         get{};  
  16.     }  
  17.   
  18.     IContainer<Phone> IContainer<Phone>.Items  
  19.     {  
  20.         set{};  
  21.         get{};  
  22.     }  
  23.   
  24.     IContainer<Email> IContainer<Email>.Items  
  25.     {  
  26.         set{};  
  27.         get{};  
  28.     }  
  29. }  


*构造器和终结器

泛型的构造器和构造器不要求添加类型来与类的声明匹配。

[csharp] view plaincopy
  1. public struct Pair<T>:IPair<T>  
  2. {  
  3.     public Pair(T first,T second)  
  4.     {  
  5.         _Second=second;  
  6.         _First=first;  
  7.     }  
  8.   
  9.     //...  
  10. }  


*默认值的指定:default关键字

在上述代码中,省略了属性的定义。因为在struct中,属性应该被全部初始化。可以使用default运算符来初始化一个字段,以便初始化属性。

[csharp] view plaincopy
  1. public struct Pair<T>:IPair<T>  
  2. {  
  3.     public Pair(T first,T second)  
  4.     {  
  5.         _Second=second;  
  6.         _First=default(T);//使用default运算符  
  7.     }  
  8.   
  9.     //...  
  10. }  

**多个类型参数

[csharp] view plaincopy
  1. //多个参数类型的泛型接口  
  2. interface Ipair<TFirst,TSecond>  
  3. {  
  4.     TFirst first { setget; }  
  5.     TSecond second { setget; }  
  6. }  
  7.   
  8. //实现一个多个参数类型的泛型接口  
  9. public struct Pair<TFirst,TSecond> : Ipair<TFirst,TSecond>  
  10. {  
  11.     public Pair(TFirst first, TSecond second)  
  12.     {  
  13.         _First = first;  
  14.         _Second = second;  
  15.     }  
  16.     public TFirst first  
  17.     {  
  18.         get  
  19.         {  
  20.             return _First;  
  21.         }  
  22.         set   
  23.         {  
  24.             _First = value;   
  25.         }  
  26.     }  
  27.     private TFirst _First;  
  28.   
  29.     public TSecond second  
  30.     {  
  31.         get  
  32.         {  
  33.             return _Second;  
  34.         }  
  35.         set  
  36.         {  
  37.             _Second = value;  
  38.         }  
  39.     }  
  40.     private TSecond _Second;  
  41. }  

[csharp] view plaincopy
  1. //使用多个参数类型的泛型  
  2. Pair<int ,string> historycalEvent=new Pair<int,string>(1914,"hello world!!");  
  3. Console.WriteLine("{0}:{1}",historycalEvent.first,historycalEvent.second);  

*元数【不熟】

在C#4.0中,CLR定义了9个新的泛型类型:Tuple。因为元数不同(每个类都有不同数量的类型参数)可以使用隐式类型参数来创建

public class Tuple{}

public class Tuple<T1>:IStructuralEquatable,IStructuralComparable,IComparable{}

public class Tuple<T1,T2>:……{}

.....

public class Tuple<T1,T2,T3,T4...T7,TReset>:...{}

Tuple <string ,Contact> keyValuePair;

//当Tuple变大时,creat()工厂方法有明显的优势

keyValuePair=Tuple.Create("5555",new Contact("Inigo Montaya"));

keyValuePair=new Tuple<string,Contact>("5555",new Contact("Inigo MOntaya"));


*嵌套泛型:使用规则与嵌套类规则一样。


*下面用于泛型类参数的类型称为无绑定(unboundded)类型,因为没有对他们进行任何约束。通过约束,可以把类型限制为实例化泛型类。

[csharp] view plaincopy
  1. public class BinaryTree<T>  
  2. {  
  3.     public BinaryTree(T item)  
  4.     {  
  5.         Item = item;  
  6.     }  
  7.   
  8.     public T Item  
  9.     {  
  10.         get;  
  11.         set;  
  12.     }  
  13.   
  14.     public Pair<BinaryTree<T>> SubItems  
  15.     {  
  16.         get{return _SubItem;}  
  17.         set  
  18.         {  
  19.             IComparable<T> first;  
  20.             //Error:如果没有IComparable<T>,value.first.Item看做是Object类,没有CompareTo的方法,必须显示转换;  
  21.             //虽然现在没有编译错误,但是如果声明一个BinaryTree类的变量,然后没有提供一个没有实现IComparable<T>接口的类型参数,  
  22.             //就会发生执行时错误。  
  23.             //为了避免这一点错误,C#允许泛型类中声明的每一个类型参数提供一个可选的约束列表。参数的要求用于限制从中派生的类或接口,  
  24.             //或者限制必须存在一个默认构造器,或者限制使用一个引用/值类型约束。  
  25.             first = (IComparable<T>)value.first.Item;  
  26.             if (first.CompareTo(value.second.Item) < 0)  
  27.             {  
  28.                 //first is less than second;  
  29.             }  
  30.             else  
  31.             {  
  32.                 //first is large or equal to second.  
  33.             }  
  34.             _SubItem = value;  
  35.         }  
  36.     }  
  37.     private  Pair<BinaryTree<T>> _SubItem;  
  38. }  

*在类定义中,可以使用where关键字类实现。where T:constraint1,constraint2,约束必须在继承说明符后面。


class MyGenericClass<T> where T:constraint1,constraint2

{//...

}

可有有多个where,多个where之间不存在逗号,多个where是&& 关系

class MyGenericClass<T1,T2> where T1:constraint1 where T2:constraint2

{//....

}

**可用约束:

struct

class

base-class 类型必须是基类或者继承自基类。可以给这个约束提供任意类名。

interface 类型必须是接口或者实现了接口

new() 类型必须有一个公共的无参构造函数,必须是类型指定的最后一个约束。

**接口约束:规定某个数据类型必须实现某个接口,需要声明一个接口约束。有了这种约束之后,甚至不需要执行转型,就可以调用一个显式的接口成员实现。

在上述代码中,为解决执行错误可以使用接口约束。确保T类型参数实现了ICompareTo<T>接口。

public class BinaryTree<T> where T:System.IComparable<T>

first = value.first.Item;  //这句话也不会出现编译错误。

添加上述接口约束之后,编译器会确保每次使用BinaryTree类的时候,都必须指定一个实现了IComparable<T>接口的类型参数。

除此之外,在调用CompareTo()方法之前,不需要显示转型。

**基类约束:需要将构建的类型限制为一个特定的派生类。积累约束与接口约束基本相同,但是,假如同时指定了多个约束,那么基类约束必须第一个出现。和接口不同的是,多个基类约束是不可能的,因为类不能多继承。不能为sealed类或者某些特殊类型的结构指定基类约束。

//有Cow和Chicken类继承自Animal类

MyGenericClass<Cow> =new MyGenericClass<Cow>;//OK

MyGenericClass<String>=new MyGenericClass<String>;//不能编译

**struct /class 约束:限制参数类型为一个值类型或者引用类型。编译器不允许在约束中将System.ValueType指定为基类。而是用关键字struct或者class来指定值类型或者引用类型。

public struct Nullable<T>:IFormattable,IComparable,IComparable<Nullable<T>>,INullablewhere T:Struct  //类型参数必须是一个值类型

{...

}

struct约束中,禁止将System.Nullable<T>作为参数类型。禁止  int??number;

**构造器约束:它指定的类型参数必须有一个默认构造器,只能对默认构造器进行约束,不能为带参数的构造器指定约束。

[csharp] view plaincopy
  1. public class EntityBase<Tkey>  
  2. {  
  3.     public Tkey Key  
  4.     {  
  5.         get;  
  6.         set;  
  7.     }  
  8. }  
  9.   
  10. public class EntityDictionary<Tkey, Tvalue>:  
  11.     Dictionary<Tkey,Tvalue>  
  12.     where Tkey :IComparable<Tkey>,IFormattable  
  13.     where Tvalue :EntityBase<Tkey>,new()   //使用new()  
  14. {  
  15.     //...  
  16.     public Tvalue New(Tkey key)  
  17.     {  
  18.         Tvalue newEntity = new Tvalue();//默认的构造函数  
  19.         newEntity.Key = key;  
  20.         Add(newEntity.Key, newEntity);//继承了Dictionary所以可以直接使用方法。  
  21.         return newEntity;  
  22.     }  
  23. }  

**约束继承

约束可以由一个派生类继承,但是必须在派生类中显示的指定这些约束。在继承的情况下,不仅可以保留基类本来的约束(必须),还可以添加额外的约束。

**可以通过base-class约束,把一个参数用作另一个参数的的约束

//其中,T2必须与T1的类型相同,或者继承自T1。这成为裸类型约束(naked type constraint),表示为一个泛型类型参数用作另一个类型参数的约束

class MyGenericClass<T1,T2> where T2:T1

{...

}

//类型约束不能循环

class MyGenericClass<T1,T2>where T2:T1 where T1:T2  //Error

{...

}

[csharp] view plaincopy
  1. //Farm是一个泛型类Farm<T>,没有继承List类  
  2. class Farm<T>:IEnumerable<T> where T:Animal  
  3. {  
  4.     //字段  
  5.     private List<T> animals = new List<T>();  
  6.     //属性Animals定义为泛型List类,且被Animal约束或者继承自Animal。  
  7.     public List<T> Animals  
  8.     {  
  9.         get  
  10.         {  
  11.             return animals;  
  12.         }  
  13.     }  
  14.     //隐式接口实现 实现了了接口的方法  
  15.     public IEnumerator<T> GetEnumerator()  
  16.    {  
  17.        return animals.GetEnumerator();  
  18.    }  
  19.     //显示接口实现,实现IEnumerable.GetEnumerator()  
  20.     IEnumerator IEnumerable.GetEnumerator()  
  21.     {  
  22.         return animals.GetEnumerator();  
  23.     }  
  24.     //利用了抽象类Animal的方法  
  25.     public void MakeANoises()  
  26.     {  
  27.         foreach (T animal in animals)  
  28.         {  
  29.             animal.MakeANoise();  
  30.         }  
  31.     }  
  32.   
  33.     public void FeedTheAnimals()  
  34.     {  
  35.         foreach (T animal in animals)  
  36.         {  
  37.             animal.Feed();  
  38.         }  
  39.     }  
  40.   
  41.     public Farm<Cow> GetCows()  
  42.     {  
  43.         Farm<Cow> cowFarm = new Farm<Cow>();  
  44.         foreach (T animal in animals)  
  45.         {  
  46.             if (animal is Cow)  
  47.             {  
  48.                 cowFarm.Animals.Add(animal as Cow);  
  49.             }  
  50.         }  
  51.         return cowFarm;  
  52.     }  
  53. }  

**从泛型类中继承

如果某个类型在它新继承的基类型中受到约束,该类型就不能“解除约束”。也就是说,类型T在所继承的基类型中,该类型必须受到与基类型相同的约束。

下面的代码能运行:

class SuperFarm<T>:Farm<T>

where T:SuperCow   //SuperCow是Animal的子集,而T受到的是Animal。所以可以编译。

下面是代码不能运行:

class SuperFarm<T>:Farm<T>

where T:struct

如果继承了一个泛型类型,就必须提供所有必须的类型信息,这可以使用其他泛型类参数的形式来提供。

public class Cards:List<Card>,ICloneable  //编译成功。

{...

}

public class Cards:List<T>,ICloneable //编译错误,因为没有提供T的信息

{....

}

*不允许 :委托和枚举类型的约束是无效的。

不支持OR条件。

不允许运算符约束。

**泛型方法:即使包容类不是泛型类,或者方法包含的类型参数不在泛型类的类型参数列表中,也依然使用泛型的方法。为了定义泛型方法,需要紧接在方法名之后添加类型参数语法。

[csharp] view plaincopy
  1. //泛型方法  
  2. public static class MathEx  
  3. {  
  4.     public static T Max<T>(T first, params T[] values)  
  5.         where T : IComparable<T>  
  6.     {  
  7.         T maximum = first;  
  8.         foreach (T item in values)  
  9.         {  
  10.             if (item.CompareTo(maximum) > 0)  
  11.             {  
  12.                 maximum = item;  
  13.             }  
  14.         }  
  15.   
  16.         return maximum;  
  17.     }  
  18.   
  19.     public static T Min<T>(T first, params T[] values)  
  20.         where T : IComparable<T>  
  21.     {  
  22.         T minimum = first;  
  23.         foreach (T item in values)  
  24.         {  
  25.             if (item.CompareTo(minimum)< 0)  
  26.             {  
  27.                 minimum = item;  
  28.             }  
  29.         }  
  30.   
  31.         return minimum;  
  32.     }  
  33. }  
*类型推断

[csharp] view plaincopy
  1. //显示指定类型参数  
  2. Console.WriteLine(MathEx.Max<int>(7,409));  
  3. Console.WriteLine(MathEx.Min<string>("Rous","Find"));  
  4. //类型推断  
  5. Console.WriteLine(MathEx.Max(88, 409));  
  6. Console.WriteLine(MathEx.Min("aRous""aFind"));  
  7. Console.ReadKey();  
**约束的指定

泛型方法也可以指定约束,将where放在方法头的后面,{} 前面。

***协变形和逆变性:

如果用不同的类型参数声明同一个泛型,变量不是类型兼容的,即使是讲一个较具体的类型付给一个比较泛化的类型,就说他们是协变量(covariant)。同时,编译器禁止逆变性(contravariance).换言之,就是不允许将教泛化的类型赋给教具体的类型。

***在C#4.0中使用out类型参数修饰符允许协变性。【只读】

***在C#4.0中使用in类型参数修饰符允许逆变性。【只写】

//在单个泛型类中合并协变性和逆变性。

interface TConvertible <in TSource, out TTarget>

{

TTarget Convert(TSource source);

}












原创粉丝点击