C#高级编程笔记 Day 6, 2016年9月 14日 (泛型)
1、泛型类的功能:在创建泛型类时,还需要一些其他C#关键字。例如,不能把null赋予泛型类型。此时,可以使用default 关键字。如果泛型类型不需要Object类的功能,但需要调用泛型类上的某特定方法,就可以定义约束。
具体讨论以下:
- 默认值
- 约束
- 继承
- 静态成员
例:下面是一个使用 泛型文档管理器示例。DocumentManager
using System;
using System.Collections.Generic;
namespace Jandar.Test.YinXi{
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>;
}
}
}
除了IEnumerable 接口,还有一个泛型版本 IEnumerable<T>。IEnumerable<T> 派生自IEnumerable 添加了IEnumerator<T> 的GetEnumerator() 方法,LinkeedList<T>实现泛型接口 IEnumerable<T>
【默认值】
给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;
【约束】
- 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{getl;set;}
} - 要使用DocumentManager<T> 类显示文档,可以将类型 T 强制转换为 IDocument 接口,以显示标题 title 或者 内容 content
public void DisplayAllDocuments()
{
foreach(T doc in documentQueue)
{
Console.WriteLine(((IDocument)doc).Title);
}
}- 问题是如果类型T没有实现IDocument 接口,这个类型强制转换就会导致一个运行异常。最好给DocumentManager<TDocument>类定义一个约束: TDocument 类型必须实现 IDocument 接口,
public class DocumentManager<TDocument> where TDocument : IDocument
{
...
}约束 说明 where T : struct
对于结构约束,类型 T 必须是值类型 where T : class
类约束指定类型 T 必须是引用类型 where T : IFoo
指定类型 T 必须实现接口 IFoo where T : Foo
指定类型 T 必须派生自基类 Foo where T : new()
这是一个构造函数约束,指定类型 T 必须有一个默认构造函数
where T1 : T2
这个约束也可以指定,类型T1 派生自 泛型类型T2。 该约束也成为裸类型约束 【只能为默认构造函数定义构造函数约束,不能为其他构造函数定义构造函数约束】
使用泛型类型还可以合并多个约束。 where T :IFoo,new() 约束和 MyClass<T>声明指定,类型T 必须实现IFoo 接口,且必须有一个默认构造函数。
-
public class MyClass<T> where T :IFoo, new()
{
...
}
-
【继承】
可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现。
-
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;
}
}
-
【静态成员】
泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。
-
public class StaticDemo<T>
{
public static int x;
} //由于同时对一个string类型和一个int 类型使用了 StaticDemo<T> 类。所以存在两组静态字段。
StaticDemo<string>.x=;
StaticDemo<int>.x=;
Console.WriteLine(StaticDemo<string>.x);//
2、泛型接口
【协变和抗变】
协变和抗变指对参数和返回值的类型进行转换。
在.NET 中,参数类型是协变的。假定有 Shape 和 Rectangle 类,Rectangle 派生自Shape基类。声明 Display()方法是为了接受Shape 类型的对象作为其参数:
public void Display(Shape o){ }
方法的返回类型是抗变的。当方法返回一个Shape 时,不能把它赋予 Rectangle, 因为Shape不一定总是Rectangle。反过来是可行的:如果一个方法像 GetRectangle() 方法那样返回一个Rectangle,
public Rectangle GetRectangle();
就可以把结果赋予某个Shape
Shape s=GetRectangle();
在 .NET Framework4 版本之前,这种行为方式不适用于泛型。 在C# 4 中,扩展后的语言支持泛型接口和泛型委托的协变和抗变。
泛型接口的协变
如果泛型类型用 out 关键字标注,泛型接口就是协变的。这也意味着返回类型只能是 T 。 接口 IIndex 与 类型 T 是协变的,并从一个只读索引器中返回这个类型。
public interface IIndex<out T>
{
T this[int index] { get;}
int Count{get;}
}
C#高级编程笔记 Day 6, 2016年9月 14日 (泛型)的更多相关文章
- 2016年12月14日 星期三 --出埃及记 Exodus 21:9
2016年12月14日 星期三 --出埃及记 Exodus 21:9 If he selects her for his son, he must grant her the rights of a ...
- 2016年11月14日 星期一 --出埃及记 Exodus 20:5
2016年11月14日 星期一 --出埃及记 Exodus 20:5 You shall not bow down to them or worship them; for I, the LORD y ...
- 2016年10月14日 星期五 --出埃及记 Exodus 18:25
2016年10月14日 星期五 --出埃及记 Exodus 18:25 He chose capable men from all Israel and made them leaders of th ...
- 关于苹果开发证书失效的解决方式(2016年2月14日Failed to locate or generate matching signing assets)
前言: 从2月14日開始,上传程序的同学可能会遇到提示上传失败的提示. 而且打开自己的钥匙串,发现所有的证书所有都显示此证书签发者无效. Failed to locate or generate ma ...
- 2016年11月14日--SQL创建数据库、表-查、插、删、改
--创建数据库(create database 数据库名)create database hq20161114go --使用选择数据库(use 数据库名)use hq20161114go --创建学生 ...
- Android高级编程笔记(四)深入探讨Activity(转)
在应用程序中至少包含一个用来处理应用程序的主UI功能的主界面屏幕.这个主界面一般由多个Fragment组成,并由一组次要Activity支持.要在屏幕之间切换,就必须要启动一个新的Activity.一 ...
- 我的Python成长之路---第八天---Python基础(24)---2016年3月5日(晴)
多线程编程 什么是多线程,线程是操作系统能够进行运算调度的最小单位.他包含在进程之中,是进程中的实际运作单位.线程是进程中一个单顺序的空值六,一个进程可以并发多个线程,每个线程可以并行处理不同的任务. ...
- 我的Python成长之路---第七天---Python基础(22)---2016年2月27日(晴)
socket网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...
- 2016年2月16日开始,每天一篇,记录学习心得,【基本技能篇】>>开篇《如何阅读一本书——心得》
如何阅读一本书——心得 ——2016年2月12日 要达到阅读的所有目的,就必须在阅读不同书籍的时候,运用适当的不同速度.读的太快或太慢,都一无所获. 四个阅读层次:①基础阅读,具有基本阅读的能力,包括 ...
随机推荐
- Linux下部署docker记录(1)-Volume使用
之前部署了Linux下部署docker记录(0)-基础环境安装,接下来看看Docker Volume的使用. Docker volume使用1)一个数据卷是一个特别指定的目录,该目录利用容器的UFS文 ...
- 操作系统Unix、Windows、Mac OS、Linux的故事
电脑,计算机已经成为我们生活中必不可少的一部分.无论是大型的超级计算机,还是手机般小巧的终端设备,都跑着一个操作系统.正是这些操作系统,让那些硬件和芯片得意组合起来,让那些软件得以运行,让我们的世界在 ...
- tfs2012迁移,只用到源代码管理
背景:在虚拟机里面安装的tfs,后来发觉C盘空间太少了,运行卡,准备重新配置一台虚拟机当做tfs服务器.安装相同版本的tfs.数据库(至少比原来的版本一样或者更高版本,要不附加不了数据库). 1.确保 ...
- php 单双引号的区别
在PHP中,字符串的定义可以使用英文单引号' ',也可以使用英文双引号" ". 但是必须使用同一种单或双引号来定义字符串,如:'Hello World"和"He ...
- sql server cross/outer apply 用法
这是 sql server 帮助文档关于apply的描述: 使用 APPLY 运算符(2005或以上版本)可以为实现查询操作的外部表表达式返回的每个行调用表值函数.表值函数作为右输入,外部表表达式作为 ...
- 多说评论系统API调用和本地身份说明(JWT)
多说评论系统是一个非常好用的第三方评论插件,聚合了大多数的SNS平台账号登录和分享功能,UI也很不错. 作为网站快速接入评论系统,多说是一个比较好的选择,其也提供了一些实用的API去满足定制化需求. ...
- python-实现生产者消费者模型
生产者消费者:包子铺不停的做包子,行人不停的买 ---> 这样就达到了目的--->包子的销售 两个不同的角色 包子铺,行人 只负责单一操作 让包子变成连接的介质. #_*_coding:u ...
- MFC的多线程操作
记得用MFC做了一个图像自动修复软件,当时没有多线程操作这一概念,由于图像修复算法比较复杂,因此,当执行图像修复时,程序就像卡死了似得而不能做其他操作.其实MFC对这种情况有一种很好地解决方案,那就是 ...
- inference和learning
一开始对于机器学习,主要是有监督学习,我的看法是: 假定一个算法模型,然后它有一些超参数,通过喂多组数据,每次喂数据后计算一下这些超参数.最后,数据喂完了,参数取值也就得到了.这组参数取值+这个算法, ...
- bzoj 4318 OSU!
期望dp. 考虑问题的简化版:一个数列有n个数,每位有pi的概率为1,否则为0.求以每一位结尾的全为1的后缀长度的期望. 递推就好了. l1[i]=(l1[i-1]+1)*p[i]+0*(1-p[i] ...