C#基础系列—泛型(Generics)(下)
(转载请注明--Huwz)
我们已经知道,泛型包括了类、结构、接口、方法和委托。除此之外,泛型还可以使用约束(Constain)。使用泛型约束自然有其意义,且看下文说来。
通过C#基础系列—泛型(Generics)(上)的介绍,我相信大家应该很容易写出普通的泛型了。因此在这里我就不详细介绍了。对泛型的类、结构、接口、方法和委托我各写出一个例子。
public
class MyClasskkk<T>
{
T member = default(T);
}
|
泛型类
应用泛型的字段
|
public
struct MyStruct<T>
{
T member;
}
|
泛型结构
应用泛型的字段
|
public
interface IMyInterface<T>
{
void MyMethod<M>(M obj);
}
|
泛型接口
泛型方法(不一定使用接口提供的泛型)
|
public
delegate T MyHandle<T>(T obj);
|
泛型委托
|
l 值类型/引用类型约束
l 基类约束
l 接口约束
l 构造约束
说起约束,总给我一种不自在的感觉——我为什么非要让我的类型是从某个类型或接口继承下来呢?这样不是限制了我使用的类型了吗,“泛”型都不泛了,还有什么意义?
其实,约束从使用的感觉上来说,确实是牺牲了一些自由度,但是却换回了更有意义的可操作性。
看看下面的代码
public abstract class MyBaseClass
{
public abstract void MethodFoo();
public abstract void MethodBar();
}
public class MyGenericClass<T> where T:MyBaseClass
{
//...
}
我们的泛型类MyGenericClass的T被约束成MyBaseClass类型,除了MyBaseClass和MyBaseClass的继承类之外,T不能使用其它任何类型了。这就是泛型的约束。
但是我们也能获得约束带来的好处。我们知道,在C#中,所有的类型都是从Object继承下来的,没有使用约束的泛型,可以等价为约束为Object的泛型。
因此这些类型只能提供Object里的一些方法,比如ToString(),GetHashCode(),Equal(),GetType()。但是当我们需要实现某些其他操作的时候,这些泛型的对象就无能为力了。
但是如果我们使用的是带约束的泛型,我们就可以拥有约束类型中的方法。比如:
public class MyGenericClass<T> where T:MyBaseClass
{
public MyGenericClass()
{
T obj = default(T);
obj.MethodBar();
obj.MethodFoo();
}
}
Ok
,让我们看看怎么这四种约束类型都有什么样的特点吧
约束
|
示例
|
说明
|
值类型/
引用类型约束
|
class MyClass<T> where T: class
|
T
类型必须是引用类型
|
class MyClass<T> where T: struct
|
T
类型必须是值类型
| |
基类约束
|
class MyClass<T> where T: MyBaseClass
|
T
类型必须是MyBaseClass或从MyBaseClass继承下来
|
接口约束
|
class MyClass<T> where T: IMyInterface
|
T
类型必须是应用了IMyInterface接口的类型
|
构造约束
|
class MyClass<T> where T: new()
|
T
类型必须包含一个不带参数的构造函数
|
补充一点,在构造约束里,似乎只有这一种类型;没有任何带参的构造约束。
在泛型这些约束里,估计比较常见的是IComparable接口约束。因为应用泛型最多的是集合,当涉及到给集合里的元素排序的时候,应用IComparable接口就比较合理了。
如果有多个约束的情况下,那就这么写就可以了
class MyClass<T,U>
where T: new()
where U: class
{
//
…
}
泛型的继承
继承很简单,不过要看清楚:
假如有这么两个类: BaseClass 和 DerivedClass,且DerivedClass继承自BaseClass。还有一个泛型类MyClass<T>。那么假如我这么写——
MyClass<BaseClass> obj = new MyClass<DerivedClass>();
有没有问题?
使用基类表示继承类的对象,会有问题吗?这话说的没错,但此处却不适用。
这行代码的等号左右,实际上是表示两个完全不同的类。因此编译时肯定出错。看仔细了,这并不是泛型的继承。
其实,泛型类的继承和普通的类继承很相似
public
class MyClass<T> : MyBaseClass<T>
{
}
类型要保留下来。基类提供的泛型类型不能超过子类的泛型类型。
也就是说,可以使泛型类继承自非泛型类
public
class MyClass2<T> : MyBaseClass2
{
}
但是不允许非泛型类继承自泛型类
public
class MyABC : MyClass2<T>
{
}
其它的情况在理解上可以同理一下。比如说
public
class MyClass<T> : MyBaseClass<T,U>
{
}
这个不行,因为多了个U,所以就可以同理非泛型继承泛型。
public
class MyABC<T> : MyClass2<int>
{
}
这个可以,因为指定了泛型的类型,所以相当于固定类型。
public
class MyBaseClass2<U,T>: MyBaseClass<T,U>
{
}
这个当然也没有问题。从来都没有说泛型还有顺序的规定。
其它
有时候泛型方法和普通方法可能会产生误会,比如下面这个小测验。虽然一般来讲不推荐把代码写成这样,但有时候难免会碰到这样的麻烦。所以我要再此补充一个“硬道理”——是泛型用泛型,有非泛型可用就用非泛型。下面这个小测验,大家可以尝试一下
J
class
Program
{
static void Main(string[] args)
{
A<int> a = new A<int>();
a.Test(12);
a.InternalTest(12);
}
}
class A<T>
{
public void InternalTest(T obj)
{
Test(obj);
}
public void Test(T obj)
{
Console.WriteLine("T");
}
public void Test(int obj)
{
Console.WriteLine("int");
}
}