泛型类的功能

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

例如:

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;

}

}

}

这是一个使用泛型文档福安里的实例.文档管理器用于从队列中读取文档.先创建一个新的控制台项目DocumentManager,并添加DocumentManager<T>类.AddDocument()方法将一个文档添加到队列中.如果队列不为空,IsDocumentAvailable只读属性就返回true.

默认值

接下来给DocumentManager<T>类添加衣蛾GetDocument()方法.

public T GetDocument()

{

T doc = default(T);

lock (this)

{

doc = documentQueue.Dequeue();

}

return doc;

}

在这个方法中,应该把类型T指定为null.但是不能把null赋予泛型类型.隐隐是泛型类型也可以实例化为值类型,而null只能用于引用类型.为了解决这个问题,使用了default关键字,通过default关键字,将null赋予引用类型,将0赋予值类型.

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

约束

如果泛型类型需要调用泛型类型中的方法,就必须加以约束.对于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;

}

public string Title { get; set; }

public string Content { get; set; }

}

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

public void DisplayAllDocument()

{

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语句,从而使类型TDocument包含属性TItle. Visual Studio InterlliSense和编译器都会提供这个支持.

public void DisplayAllDocument()

{

foreach (TDocument doc in documentQueue)

{

Console.WriteLine(doc.Title);

}

}

在main()方法中,用Document类型实例化Documentmanager

<T>类,而Document类型实现了需要IDocument接口.接着添加和显示新文档,检索其中一个文档:

static void Main()

{

var dm= new Documentmanager<Document>();

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

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

dm.DisplayAllDocument();

if (dm.IsDocumentAvailable)

{

Document d=dm.GetDocument();

Console.WriteLine(d.Content);

}

}

DocumentManager现在可以处理任何实现了IDocument接口的类.

在示例应用程序中,介绍了接口约束.泛型支持集中约束类型,如下表:

约束

说明

where T :struct

对于结构约束,类型T必须是值类型

where T:class

类约束制定类型T必须是引用类型

where T:IFoo

制定类型T必须实现接口IFoo

whereT:Foo

制定类型T必须派生自基类Foo

whereT:new()

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

where T:T2

这个约束也可以指定,类型T1派生自泛型类型T2.该约束也称为裸约束

只有为默认构造函数定义构造函数约束,不能为其他构造函数定义构造函数约束.

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

public class MyClass<T>

where T:IFoo,new()

{

//..

}

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

以上案例的完整代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace ConsoleApplication9

{

class Program

{

static void Main(string[] args)

{

var dm = new DocumentManager<Document>();

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

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

dm.DisplayAllDocument();

if (dm.IsDocumentAvailable)

{

Document d = dm.GetDocument();

Console.WriteLine(d.Content);

}

Console.ReadKey();

}

}

public class DocumentManager<TDocument>

where TDocument : IDocument

{

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

public void AddDocument(TDocument doc)

{

lock (this)

{

documentQueue.Enqueue(doc);

}

}

public bool IsDocumentAvailable

{

get

{

return documentQueue.Count > 0;

}

}

public TDocument GetDocument()

{

TDocument doc = default(TDocument);

lock (this)

{

doc = documentQueue.Dequeue();

}

return doc;

}

public void DisplayAllDocument()

{

foreach (TDocument doc in documentQueue)

{

Console.WriteLine(doc.Title);

}

}

}

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;

}

public string Title { get; set; }

public string Content { get; set; }

}

}

继承

前面创建的LinkedList<T>类实现了IEnumerable<out T>接口:

public class LinkedList<T>:IEnumerable<out 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 IntCalc : Calc<int>

{

public override int Add(int x, int y)

{

return x + y;

}

public override int Sub(int x, int y)

{

return x - y;

}

}

静态成员

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

public class StaticDemo<T>

{

public static int x;

}

在Main()函数中的代码如下:

static void Main(string[] args)

{

StaticDemo<string>.x = 4;

StaticDemo<int>.x = 5;

Console.WriteLine(StaticDemo<string>.x);

Console.ReadKey();

}

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

C#编程(二十八)----------泛型类的功能的更多相关文章

  1. (NO.00001)iOS游戏SpeedBoy Lite成形记(二十八):增加排行榜功能

    游戏大体上基本也就完成了,还差一个排行榜.否则如何激励各位选手创造新纪录呢? 排行榜功能也没什么难的,不过需要一点点排序的算法上的考虑. 这里我们把排行榜记录数据和排序都放在GameState类中,在 ...

  2. Android开发(二十八)——基础功能函数

    /** * 判断事件是否在控件中 * * @param view * @param ev * @return * @see http://m.blog.csdn.net/blog/aygxylxk/8 ...

  3. Web 前端开发人员和设计师必读文章推荐【系列二十八】

    <Web 前端开发精华文章推荐>2014年第7期(总第28期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  4. VMware vSphere 服务器虚拟化之二十八 桌面虚拟化之安装View传输服务器

    VMware vSphere 服务器虚拟化之二十八 桌面虚拟化之安装View传输服务器 View 传输服务器用于管理和简化数据中心与在最终用户本地系统上检出使用的 View 桌面之间的数据传输.必须安 ...

  5. (转载)Android项目实战(二十八):Zxing二维码实现及优化

    Android项目实战(二十八):Zxing二维码实现及优化   前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中 ...

  6. (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例

    Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21我要评论 这篇文章主要介绍了Android项目 ...

  7. 剑指Offer(二十八):数组中出现次数超过一半的数字

    剑指Offer(二十八):数组中出现次数超过一半的数字 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn. ...

  8. 使用Typescript重构axios(十八)——请求取消功能:总体思路

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  9. 使用Typescript重构axios(二十八)——自定义序列化请求参数

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  10. Bootstrap <基础二十八>列表组

    列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...

随机推荐

  1. redis,nodejs,php,pub/sub 实战: 微信语音识别

    2015年5月22日 20:20:20 星期五 效果: 这边对微信说话,  浏览器端及时显示语音识别的文字 注意: 在连接socket.io时, 按下浏览器f12, 如果一直有请求不断的刷, 说明so ...

  2. 测试开发之前端——No3.HTML5中的标准属性

    HTML5的标准属性 属性 值 描述 accesskey character 规定访问元素的键盘快捷键 class classname 规定元素的类名(用于规定样式表中的类). contentedit ...

  3. **如何让CI框架支持service层

    http://www.bitscn.com/pdb/php/201411/404708.html 大家知道CodeIgniter框架式MVC分层的,通常大家把业务逻辑写到Controller中,而Mo ...

  4. Intellij Idea启用Git可视化界面

    第一步. 第二步. 然后点击OK 验证 

  5. 《NodeJS开发指南》第五章微博实例开发总结

    所有文章搬运自我的个人主页:sheilasun.me <NodeJS开发指南>这本书用来NodeJS入门真是太好了,而且书的附录部分还讲到了闭包.this等JavaScript常用特性.第 ...

  6. Webpack按需加载一切皆模块

    前言 在学习 Webpack 之前,我们需要了解一个概念:模块. 何为模块? 如果你曾学过 Java , C# 之类的语言,一定会知道 Java 中的 import 或 C# 中的 using 吧? ...

  7. Vue.js开始第一个项目

    前端架构之路:使用Vue.js开始第一个项目   Vue.js做为目前前端最热门的库之一,为快速构建并开发前端项目多了一种思维模式.本文通过一个简单的实例开始上手Vue.js开发. 一.技术准备 使用 ...

  8. JS几种变量交换

    JS几种变量交换方式以及性能分析对比   原文 原文是自己博客上发布的JS几种变量交换方式以及性能分析对比 前言 “两个变量之间的值得交换”,这是一个经典的话题,现在也有了很多的成熟解决方案,本文主要 ...

  9. Java多线程及并发

    进程:它是内存中的一段独立的空间. 线程:位于进程中,负责当前进程中的某个具备独立运行资格的空间. 进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行.一个进程中至少应该有一个线程. 多 ...

  10. PHP 数字序数&字母序数 相互转化

    序数从1开始  即 A=1  而非 A=0 /** * 数字序列转字母序列 * @param $int * @param int $start * @return string|bool */ fun ...