*泛型类型概述
参数化类型:
*可空类型也是泛型。
- //System.Nullable<int> nullableInt;
- int? op1 = 5;
- //int result = op1 * 5;无法将int?隐式转换为int,需要显示转换。
- //如果op1是null,就会生成System.InvalidOperationException类型的异常。
- int result = (int)op1 * 5;
- Console.Write(result);
- Console.ReadKey();
- //除了bool?之外的简单可控类型,该造作的结果都是null.对于bool?为& 和|操作会得到非空值。
- //op1 op2 & |
- //true null null true
- //false null false null
- // null null null null
利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构。使特定泛型类型的每个变量都有相同的内部算法,但数据类型和方法签名可以不同。
用<>来表示要处理的数据类型。
*System.Collection.Generic名称空间中包含的类型有:
List<T> :T类型对象的集合
Dictionary<K,V> :与K类型的键值相关的V类型的项的集合
*泛型接口和struct
- //泛型接口
- interface Ipair<T>
- {
- T first { set; get; }
- T second { set; get; }
- }
- //实现一个泛型接口
- public struct Pair<T> : Ipair<T>
- {
- public T first
- {
- get
- {
- return _First;
- }
- set
- {
- _First = value;
- }
- }
- private T _First;
- public T second
- {
- get
- {
- return _Second;
- }
- set
- {
- _Second = value;
- }
- }
- private T _Second;
- }
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处的项
- //有Animal,Cow和Chicken类,其中Animal是基类,有Feed()方法
- List<Animal> animalCollection = new List<Animal>();
- animalCollection.Add(new Cow("Jack"));
- animalCollection.Add(new Chicken("Vera"));
- foreach (Animal myAnimal in animalCollection)
- {
- myAnimal.Feed();
- }
使用泛型接口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只能用!= ,== 来运算。
- class MyGenericClass<T1, T2, T3>
- { //T1,T2,T3可以用作变量的类型,属性或方法等成员的返回类型。
- private T1 innerT1Object;
- public MyGenericClass(T1 item)
- {
- innerT1Object = item;
- }
- public T1 InnerT1Object
- {
- get
- {
- return innerT1Object;
- }
- }
- }
- public bool Compare(T1 op1, T2 op2)
- {
- if (op1 != null && op2 != null)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
*在一个类中重复一个接口实现
- public interface IContainer<T>
- {
- ICollection<T> Items
- {
- set;
- get;
- }
- }
- public class Person : IContainer<Adress>, IContainer<Phone>, IContainer<Emiall>
- {
- IContainer<Adress> IContainer<Adress>.Items //Items属性使用一个显示接口实现多次出现,每一次参数类型都有所不同。
- {
- set{};
- get{};
- }
- IContainer<Phone> IContainer<Phone>.Items
- {
- set{};
- get{};
- }
- IContainer<Email> IContainer<Email>.Items
- {
- set{};
- get{};
- }
- }
*构造器和终结器
泛型的构造器和构造器不要求添加类型来与类的声明匹配。
- public struct Pair<T>:IPair<T>
- {
- public Pair(T first,T second)
- {
- _Second=second;
- _First=first;
- }
- //...
- }
*默认值的指定:default关键字
在上述代码中,省略了属性的定义。因为在struct中,属性应该被全部初始化。可以使用default运算符来初始化一个字段,以便初始化属性。
- public struct Pair<T>:IPair<T>
- {
- public Pair(T first,T second)
- {
- _Second=second;
- _First=default(T);//使用default运算符
- }
- //...
- }
**多个类型参数
- //多个参数类型的泛型接口
- interface Ipair<TFirst,TSecond>
- {
- TFirst first { set; get; }
- TSecond second { set; get; }
- }
- //实现一个多个参数类型的泛型接口
- public struct Pair<TFirst,TSecond> : Ipair<TFirst,TSecond>
- {
- public Pair(TFirst first, TSecond second)
- {
- _First = first;
- _Second = second;
- }
- public TFirst first
- {
- get
- {
- return _First;
- }
- set
- {
- _First = value;
- }
- }
- private TFirst _First;
- public TSecond second
- {
- get
- {
- return _Second;
- }
- set
- {
- _Second = value;
- }
- }
- private TSecond _Second;
- }
- //使用多个参数类型的泛型
- Pair<int ,string> historycalEvent=new Pair<int,string>(1914,"hello world!!");
- 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)类型,因为没有对他们进行任何约束。通过约束,可以把类型限制为实例化泛型类。
- public class BinaryTree<T>
- {
- public BinaryTree(T item)
- {
- Item = item;
- }
- public T Item
- {
- get;
- set;
- }
- public Pair<BinaryTree<T>> SubItems
- {
- get{return _SubItem;}
- set
- {
- IComparable<T> first;
- //Error:如果没有IComparable<T>,value.first.Item看做是Object类,没有CompareTo的方法,必须显示转换;
- //虽然现在没有编译错误,但是如果声明一个BinaryTree类的变量,然后没有提供一个没有实现IComparable<T>接口的类型参数,
- //就会发生执行时错误。
- //为了避免这一点错误,C#允许泛型类中声明的每一个类型参数提供一个可选的约束列表。参数的要求用于限制从中派生的类或接口,
- //或者限制必须存在一个默认构造器,或者限制使用一个引用/值类型约束。
- first = (IComparable<T>)value.first.Item;
- if (first.CompareTo(value.second.Item) < 0)
- {
- //first is less than second;
- }
- else
- {
- //first is large or equal to second.
- }
- _SubItem = value;
- }
- }
- private Pair<BinaryTree<T>> _SubItem;
- }
*在类定义中,可以使用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;
**构造器约束:它指定的类型参数必须有一个默认构造器,只能对默认构造器进行约束,不能为带参数的构造器指定约束。
- public class EntityBase<Tkey>
- {
- public Tkey Key
- {
- get;
- set;
- }
- }
- public class EntityDictionary<Tkey, Tvalue>:
- Dictionary<Tkey,Tvalue>
- where Tkey :IComparable<Tkey>,IFormattable
- where Tvalue :EntityBase<Tkey>,new() //使用new()
- {
- //...
- public Tvalue New(Tkey key)
- {
- Tvalue newEntity = new Tvalue();//默认的构造函数
- newEntity.Key = key;
- Add(newEntity.Key, newEntity);//继承了Dictionary所以可以直接使用方法。
- return newEntity;
- }
- }
**约束继承
约束可以由一个派生类继承,但是必须在派生类中显示的指定这些约束。在继承的情况下,不仅可以保留基类本来的约束(必须),还可以添加额外的约束。
**可以通过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
{...
}
- //Farm是一个泛型类Farm<T>,没有继承List类
- class Farm<T>:IEnumerable<T> where T:Animal
- {
- //字段
- private List<T> animals = new List<T>();
- //属性Animals定义为泛型List类,且被Animal约束或者继承自Animal。
- public List<T> Animals
- {
- get
- {
- return animals;
- }
- }
- //隐式接口实现 实现了了接口的方法
- public IEnumerator<T> GetEnumerator()
- {
- return animals.GetEnumerator();
- }
- //显示接口实现,实现IEnumerable.GetEnumerator()
- IEnumerator IEnumerable.GetEnumerator()
- {
- return animals.GetEnumerator();
- }
- //利用了抽象类Animal的方法
- public void MakeANoises()
- {
- foreach (T animal in animals)
- {
- animal.MakeANoise();
- }
- }
- public void FeedTheAnimals()
- {
- foreach (T animal in animals)
- {
- animal.Feed();
- }
- }
- public Farm<Cow> GetCows()
- {
- Farm<Cow> cowFarm = new Farm<Cow>();
- foreach (T animal in animals)
- {
- if (animal is Cow)
- {
- cowFarm.Animals.Add(animal as Cow);
- }
- }
- return cowFarm;
- }
- }
**从泛型类中继承
如果某个类型在它新继承的基类型中受到约束,该类型就不能“解除约束”。也就是说,类型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条件。
不允许运算符约束。
**泛型方法:即使包容类不是泛型类,或者方法包含的类型参数不在泛型类的类型参数列表中,也依然使用泛型的方法。为了定义泛型方法,需要紧接在方法名之后添加类型参数语法。
- //泛型方法
- public static class MathEx
- {
- public static T Max<T>(T first, params T[] values)
- where T : IComparable<T>
- {
- T maximum = first;
- foreach (T item in values)
- {
- if (item.CompareTo(maximum) > 0)
- {
- maximum = item;
- }
- }
- return maximum;
- }
- public static T Min<T>(T first, params T[] values)
- where T : IComparable<T>
- {
- T minimum = first;
- foreach (T item in values)
- {
- if (item.CompareTo(minimum)< 0)
- {
- minimum = item;
- }
- }
- return minimum;
- }
- }
- //显示指定类型参数
- Console.WriteLine(MathEx.Max<int>(7,409));
- Console.WriteLine(MathEx.Min<string>("Rous","Find"));
- //类型推断
- Console.WriteLine(MathEx.Max(88, 409));
- Console.WriteLine(MathEx.Min("aRous", "aFind"));
- Console.ReadKey();
泛型方法也可以指定约束,将where放在方法头的后面,{} 前面。
***协变形和逆变性:
如果用不同的类型参数声明同一个泛型,变量不是类型兼容的,即使是讲一个较具体的类型付给一个比较泛化的类型,就说他们是协变量(covariant)。同时,编译器禁止逆变性(contravariance).换言之,就是不允许将教泛化的类型赋给教具体的类型。
***在C#4.0中使用out类型参数修饰符允许协变性。【只读】
***在C#4.0中使用in类型参数修饰符允许逆变性。【只写】
//在单个泛型类中合并协变性和逆变性。
interface TConvertible <in TSource, out TTarget>
{
TTarget Convert(TSource source);
}