背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-58520.html    注册17xie 一起来写书 实现您的出书梦想!

9.3  泛型类的特性

在创建泛型类时,需要一些其他C#关键字。例如,不能把null赋予泛型类型。此时,可以使用default关键字。如果泛型类型不需要Object类的功能,但需要调用泛型类上的某些特定方法,就可以定义约束。

本节讨论如下主题:

默认值

约束

继承

静态成员

下面开始一个使用泛型文档管理器的示例。文档管理器用于从队列中读写文档。先创建一个新的控制台项目DocumentManager,添加类DocumentManager<T>。AddDocument()方法将一个文档添加到队列中。如果队列不为空,IsDocumentAvailable只读属性就返回true。

using System;

using System.Collections.Generic;

namespace Wrox.ProCSharp.Generics

{

public class DocumentManager<T>

{

private readonly Queue<T> documentQueue = new Queue<T>();

public void AddDocument(T doc)

{

lock (this)

{

documentQueue.Enqueue(doc);

}

}

public bool IsDocumentAvailable

{

get { return documentQueue.Count > 0; }

}

}

}

9.3.1  默认值

现在给DocumentManager<T>类添加一个GetDocument()方法。在这个方法中,给类型T指定null。但是,不能把null赋予泛型类型。原因是泛型类型也可以实例化为值类型,而null只能用于引用类型。为了解决这个问题,可以使用default关键字。通过default关键字,将null赋予引用类型,将0赋予值类型。

public T GetDocument()

{

T doc = default(T);

lock (this)

{

doc = documentQueue.Dequeue();

}

return doc;

}

注意:

default关键字根据上下文可以有多种含义。switch语句使用default定义默认情况。在泛型中,根据泛型类型是引用类型还是值类型,default关键字用于将泛型类型初始化为null或0。

9.3.2  约束

如果泛型类需要调用泛型类型上的方法,就必须添加约束。对于DocumentManager<T>,文档的标题应在DisplayAllDocuments()方法中显示。

Document类执行带有Title和Content属性的IDocument接口:

public interface IDocument

{

string Title { get; set; }

string Content { get; set; }

}

public class Document : IDocument

{

public Document()

{

}

public Document(string title, string content)

{

this.title = title;

this.content = content;

}

private string title;

public string Title

{

get { return title; }

set { title = value; }

}

private string content;

public string Content

{

get { return content; }

set { content = value; }

}

}

要使用DocumentManager<T>类显示文档,可以将类型T强制转换为IDocument接口,以显示标题:

public void DisplayAllDocuments()

{

foreach (T doc in documentQueue)

{

Console.WriteLine((IDocument)doc).Title);

}

}

问题是,如果类型T没有执行IDocument接口,这个类型转换就会生成一个运行异常。最好给DocumentManager<TDocument>类定义一个约束:TDocument类型必须执行IDocument接口。为了在泛型类型的名称中指定该要求,将T改为TDocument。where子句指定了执行IDocument接口的要求。

public class DocumentManager<TDocument>

where TDocument : IDocument

{

这样,就可以编写foreach语句,让类型T包含属性Title了。Visual Studio IntelliSense和编译器都会提供这个支持。

public void DisplayAllDocuments()

{

foreach (TDocument doc in documentQueue)

{

Console.WriteLine(doc.Title);

}

}

在Main()方法中,DocumentManager<T>类用Document类型实例化,而Document类型执行了需要的IDocument接口。接着添加和显示新文档,检索其中一个文档:

static void Main()

{

DocumentManager<Document> dm = new DocumentManager<Document>();

dm.AddDocument(new Document("Title A", "Sample A"));

dm.AddDocument(new Document("Title B", "Sample B"));

dm.DisplayAllDocuments();

if (dm.IsDocumentAvailable)

{

Document d = dm.GetDocument();

Console.WriteLine(d.Content);

}

}

DocumentManager现在可以处理任何执行了IDocument接口的类。

在示例应用程序中,介绍了接口约束。还有几种约束类型,如表9-1所示。

表  9-1

约    束

说    明

where T : struct

使用结构约束,类型T必须是值类型

where T : class

类约束指定,类型T必须是引用类型

where T : IFoo

指定类型T必须执行接口IFoo

where T : Foo

指定类型T必须派生于基类Foo

where T : new()

这是一个构造函数约束,指定类型T必须有一个默认构造函数

where T : U

这个约束也可以指定,类型T派生于泛型类型V。该约束也称为裸类型约束

注意:

在CLR 2.0中,只能为默认构造函数定义约束,不能为其他构造函数定义约束。

使用泛型类型还可以合并多个约束。where T : IFoo,new()约束和MyClass<T>声明指定,类型T必须执行IFoo接口,且必须有一个默认构造函数。

public class MyClass<T>

where T : IFoo, new()

{

//...

提示:

在C# 2.0中,where子句的一个重要限制是,不能定义必须由泛型类型执行的运算符。运算符不能在接口中定义。在where子句中,只能定义基类、接口和默认构造函数。

9.3.3  继承

前面创建的LinkedList<T>类执行了IEnumerable<T>接口:

public class LinkedList<T> : IEnumerable<T>

{

//...

泛型类型可以执行泛型接口,也可以派生于一个类。泛型类可以派生于泛型基类:

public class Base<T>

{

}

public class Derived<T> : Base<T>

{

}

其要求是必须重复接口的泛型类型,或者必须指定基类的类型,如下所示:

public class Base<T>

{

}

public class Derived<T> : Base<string>

{

}

于是,派生类可以是泛型类或非泛型类。例如,可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现。这允许对特定类型执行特殊的操作:

public abstract class Calc<T>

{

public abstract T Add(T x, T y);

public abstract T Sub(T x, T y);

}

public class SimpleCalc : Calc<int>

{

public override int Add(int x, int y)

{

return x + y;

}

public override int Sub(int x, int y)

{

return x - y;

}

}

9.3.4  静态成员

泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。下面看一个例子。StaticDemo<T>类包含静态字段x:

public class StaticDemo<T>

{

public static int x;

}

由于对一个string类型和一个int类型使用了StaticDemo<T>类,所以存在两组静态字段:

StaticDemo<string>.x = 4;

StaticDemo<int>.x = 5;

Console.WriteLine(StaticDemo<string>.x); // writes 4


字数:4557    最后更新:7个月以前 [04-10 21:31]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:9.2 创建泛型类  [后一页]:9.4 泛型接口
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号