c#2.0比c#1.0有一点最大的改进就是加入对泛型的支持。泛型起源于c++语言的模板机制。这样在c#中就避免了不必要的拆装箱操作,而且还加强了编译时的安全性,强类型的检查。
CLR#允许创建泛型引用类型,值类型,但是不许创建泛型枚举类型。 还可以创建泛型接口,委托,以及最常见的泛型方法。
//
c#中的泛型集合类:
List < T >
Dictionary < TKey, TValue >
SortedDictionary < TKey, TValue >
Stack < T >
Queue < T >
LinkedList < T >
// c#中的泛型接口:
IList < T >
IDictionary < TKey, TValue >
IConection < T >
IEnumerator < T >
IEnumerable < T >
IComparer < T >
IComparable < T >
List < T >
Dictionary < TKey, TValue >
SortedDictionary < TKey, TValue >
Stack < T >
Queue < T >
LinkedList < T >
// c#中的泛型接口:
IList < T >
IDictionary < TKey, TValue >
IConection < T >
IEnumerator < T >
IEnumerable < T >
IComparer < T >
IComparable < T >
下面展示如何使用其中的一些泛型方法:
public
static
void
Main()
...
{
Byte[] byteArray = new Byte[] ...{5, 1, 4, 2, 3};
Array.Sort<Byte>(byteArray);
Int32 i = Array.BinarySearch<Byte>(byteArray, 1);
Console.WriteLine(i); // Displays "0"
}
Byte[] byteArray = new Byte[] ...{5, 1, 4, 2, 3};
Array.Sort<Byte>(byteArray);
Int32 i = Array.BinarySearch<Byte>(byteArray, 1);
Console.WriteLine(i); // Displays "0"
}
下面看一下泛型的同一性:
internal
sealed
class
DateTimeList : List
<
DateTime
>
...
{
}
DateTimeList dt = new DateTimeList(); // 这样写省去了<>括号,看起来简便了
/**/ /*这样会返回false,也就是如果一个方法参数允许接受DateTimeList类型,那么我们不可以把List<DateTime>传给他,因为这两个类型不相等,但是如果一个方法参数允许接受List<DateTime>类型,那么可以把一个DateTimeList传过去,因为他们之间存在继承关系*/
Boolean sameType = ( typeof (List < DateTime > ) == typeof (DateTimeList));
// 如果我们想简便写法,可以像下面这样写:
using DateTimeList = System.Collections.Generic.List < System.DateTime > ;
Boolean sameType = ( typeof (List < DateTime > ) == typeof (DateTimeList)); // 返回true
}
DateTimeList dt = new DateTimeList(); // 这样写省去了<>括号,看起来简便了
/**/ /*这样会返回false,也就是如果一个方法参数允许接受DateTimeList类型,那么我们不可以把List<DateTime>传给他,因为这两个类型不相等,但是如果一个方法参数允许接受List<DateTime>类型,那么可以把一个DateTimeList传过去,因为他们之间存在继承关系*/
Boolean sameType = ( typeof (List < DateTime > ) == typeof (DateTimeList));
// 如果我们想简便写法,可以像下面这样写:
using DateTimeList = System.Collections.Generic.List < System.DateTime > ;
Boolean sameType = ( typeof (List < DateTime > ) == typeof (DateTimeList)); // 返回true
记住显示的方法优于泛型方法被调用:
void
Swap
<
T
>
(T arg1,Targ2)
... {
Console.Write("Generic");
}
void Swap( double arg1, double ,arg2)
... {
Console.Write("Common");
}
// 下面将会调用第二个方法
Swap( 12.36 , 36.21 );
... {
Console.Write("Generic");
}
void Swap( double arg1, double ,arg2)
... {
Console.Write("Common");
}
// 下面将会调用第二个方法
Swap( 12.36 , 36.21 );
下面看一下泛型约束,这一点是c#泛型的优势:
约束分为三种:主要约束,次要约束,和构造器约束
/**/
/*主要约束:类型参数可以指定0或者1个主要的泛型约束,主要约束可以是值类型也可以是引用类型,指定引用类型时,那么就意味着参数必须是这个引用类型或者从这个引用类型派生,还有两个特殊的主要约束:即class和struct约束,下面看一下代码:*/
internal sealed class PrimaryConstraintOfStream < T > where T : Stream ... {
public void M(T stream) ...{
stream.Close();// OK
}
}
PrimaryConstraintOfStream < Stream > pcs = new PrimaryConstraintOfStream < Stream > (); // 合法
PrimaryConstraintOfStream < FileStream > pcs = new PrimaryConstraintOfStream < FileStream > (); // 合法
PrimaryConstraintOfStream < int > pcs = new PrimaryConstraintOfStream < int > (); // 非法
// class约束
internal sealed class PrimaryConstraintOfClass < T > where T : class ... {
public void M() ...{
T temp = null; //合法,因为已经约束了T为引用类型
}
}
// struct约束
internal sealed class PrimaryConstraintOfStruct < T > where T : struct ... {
public static T Factory() ...{
return new T();
}
}
internal sealed class PrimaryConstraintOfStream < T > where T : Stream ... {
public void M(T stream) ...{
stream.Close();// OK
}
}
PrimaryConstraintOfStream < Stream > pcs = new PrimaryConstraintOfStream < Stream > (); // 合法
PrimaryConstraintOfStream < FileStream > pcs = new PrimaryConstraintOfStream < FileStream > (); // 合法
PrimaryConstraintOfStream < int > pcs = new PrimaryConstraintOfStream < int > (); // 非法
// class约束
internal sealed class PrimaryConstraintOfClass < T > where T : class ... {
public void M() ...{
T temp = null; //合法,因为已经约束了T为引用类型
}
}
// struct约束
internal sealed class PrimaryConstraintOfStruct < T > where T : struct ... {
public static T Factory() ...{
return new T();
}
}
/**/
/*次要约束:一个类型参数可以指定0个或者多个次要约束。次要类型代表的是一个接口约束。还有一种辅助约束是类型参数之间必须存在某种关系。下面看个例子:*/
private static List < TBase > ConvertIList < T, TBase > (IList < T > list)
where T : TBase ... {
List<TBase> baseList = new List<TBase>(list.Count);
for (Int32 index = 0; index < list.Count; index++) ...{
baseList.Add(list[index]);
}
return baseList;
}
private static void CallingConvertIList() ... {
IList<String> ls = new List<String>();
ls.Add("A String");
IList<Object> To = ConvertIList<String, Object>(ls);
IList<IComparable> lc = ConvertIList<String, IComparable>(ls);
IList<IComparable<String>> lcs =ConvertIList<String, IComparable<String>>(ls);
IList<String> ls2 = ConvertIList<String, String>(ls);
IList<Exception> le = ConvertIList<String, Exception>(ls); // Error,string类型和Exception没有关系
}
private static List < TBase > ConvertIList < T, TBase > (IList < T > list)
where T : TBase ... {
List<TBase> baseList = new List<TBase>(list.Count);
for (Int32 index = 0; index < list.Count; index++) ...{
baseList.Add(list[index]);
}
return baseList;
}
private static void CallingConvertIList() ... {
IList<String> ls = new List<String>();
ls.Add("A String");
IList<Object> To = ConvertIList<String, Object>(ls);
IList<IComparable> lc = ConvertIList<String, IComparable>(ls);
IList<IComparable<String>> lcs =ConvertIList<String, IComparable<String>>(ls);
IList<String> ls2 = ConvertIList<String, String>(ls);
IList<Exception> le = ConvertIList<String, Exception>(ls); // Error,string类型和Exception没有关系
}
//
构造器约束:一个指定的类型实参实现了一个public无参构造器的一个非抽象类型。如果同时指定了构造器约束和struct约束,编译器会报错,因为值类型都隐式的提供了一个无参构造器*/
internal sealed class ConstructorConstraint < T > where T : new () ... {
public static T Factory() ...{
return new T(); //指定了构造器约束,我们可以肯定能够返回一个T类型的实例
}
}
internal sealed class ConstructorConstraint < T > where T : new () ... {
public static T Factory() ...{
return new T(); //指定了构造器约束,我们可以肯定能够返回一个T类型的实例
}
}
下面我们在看一些其他的问题:
private
static
void
SettingAGenericTypeVariableToDefaultValue
<
T
>
()
...
{
T temp = default(T); // OK
}
// 如果我们在不指定约束的时候想初始化T类型,那么我们必须使用default关键字,这样当T是引用类型时,temp被初始化为null,如果T为值类型temp被初始化为0
T temp = default(T); // OK
}
// 如果我们在不指定约束的时候想初始化T类型,那么我们必须使用default关键字,这样当T是引用类型时,temp被初始化为null,如果T为值类型temp被初始化为0
还有就是一些基元操作符(/,*,+,-,等等)不能应用在泛型参数上:
private
static
T Sum
<
T
>
(T num)
where
T :
struct
...
{
T sum = default(T) ;
for (T n = default(T); n < num; n++)
sum += n;
return sum;
}
// 编译器报错
• error CS0019: Operator ' < ' cannot be applied to operands of type ' T ' and ' T '
• error CS0023: Operator ' ++ ' cannot be applied to operand of type ' T '
• error CS0019: Operator ' += ' cannot be applied to operands of type ' T ' and ' T '
T sum = default(T) ;
for (T n = default(T); n < num; n++)
sum += n;
return sum;
}
// 编译器报错
• error CS0019: Operator ' < ' cannot be applied to operands of type ' T ' and ' T '
• error CS0023: Operator ' ++ ' cannot be applied to operand of type ' T '
• error CS0019: Operator ' += ' cannot be applied to operands of type ' T ' and ' T '