*泛型类型概述
参数化类型:
*可空类型也是泛型。
//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);
}