.NET 中的泛型 101

1.1.1 摘要

generic0

图1 C# 泛型介绍

      在接触泛型之前,我们编程一般都是使用具体类型(char, int, string等)或自定义类型来定义我们变量,如果我们有一个功能很强的接口,而且我们想把它提取或重构成一个通用的接口,使得该接口不仅仅适用于已定义数据类型,而是适用于更多数据类型,从而方便以后的扩展。

     泛型提供上述功能的实现,泛型其实就是提供一个数据类型的抽象层,因为它泛所以抽象,方便了我们代码的重构和提取,我们无需hard-code接口中的数据类型,而是通过一个抽象泛型类型来指定数据类型,所以泛型可以提取出一个通用的接口。接下来让我们通过具体的例子说明什么是泛型。

1.1.2 正文

1.1.1.1 没有泛型的C# 1.0

      相信许多人都使用过栈或者其他经典数据结构,而且对于栈的FILO都是娴熟于心了。假设现在要我们定义一个栈,用来存放具体数据类型,例如:int 数据,OK让我们来实现自定义栈。

/// <summary>
/// A custom stack1.
/// </summary>
public class CustomStack1
{private int _stackPointer = 0;private int[] _stackArray;public void Push(int item){Concrete implemention.}public int Pop(){Concrete implemention.}
}

      为了简洁没有完全遵守OOP的原则,这里没有使用属性和给出方法的具体实现,现在我们有了第一版可以存放int数据类型的栈。但如果需求改变保存数据类型增加string。那么我们可以怎样实现呢?没错我们只要增加一个类型为string的栈就OK了。

/// <summary>
/// A custom stack2.
/// </summary>
public class CustomStack2
{private int _stackPointer = 0;private string[] _stackArray;public void Push(int item){Concrete implemention.}public string Pop(){Concrete implemention.}
}

      很快我们就完成了第二版本的栈了,这看上去的确很简单而且实现起来很快只不过是ctrl + c和ctrl + v,但我们的代码真的那么简单好维护吗?想想其实危机四伏。我们能不能一开始就把保存的数据类型定义成为一个通用的类型呢(抽象数据类型)?----邪恶的object。

/// <summary>
/// A custom stack.
/// </summary>
public class CustomStack
{private int _stackPointer = 0;private object[] _stackArray;public void Push(object item){Concrete implemention.}public object Pop(){Concrete implemention.}
}

      现在我们实现了第三版的栈了,看上去我们好像找到了通用的解决方法,想想我们要注意一点是如果我们保存和取出数据类型为值类型时,值类型要进行boxing和unboxing操作(《.NET值类型和引用类型101》),而且还有进行数据类型检测。如果数据量不大我们可能察觉不到性能变化,但数据量大hehehe…。这就是为什么我们要使用泛型的原因之一,但更重要的原因是在进行boxing和unboxing时,我们需要提供更多信息给编译器,编译时再根据我们提供的信息进行类型检测。

1.1.1.2 C#中的泛型

      现在我们知道了.NET为什么要提供泛型,现在让我们学习什么是泛型和怎样实现吧!在C# 2.0中提供了泛型这一机制,但这可不是什么新奇的技术,因为早在C++和Java都提供泛型机制。

C#中提供五种泛型分别是:classes, structs, interfaces, delegates, and methods。

  • 泛型类

     也许有人说没有使用过C++的模板或Java的泛型,而且不了解C#中的泛型,真的是这样吗?其实我们经常都在使用泛型,相信很多人都使用过Dictionary<TKey,TValue>,没错这就是C#中提供的泛型字典,接下来我们将介绍自定义泛型类CustomStack。

generic2图2泛型类 

/// <summary>
/// A custom generic stack.
/// </summary>
public class CustomStack<T>
{private int _stackPointer = 0;private T[] _stackArray;public void Push(T item){Concrete implemention.}public T Pop(){Concrete implemention.}
}

      前面给出了泛型类CustomStack实现,我们发现只需在类名后添加<T>,并且把具体数据类型改为抽象的泛型类型T,现在我们就定义了一个简单泛型类,我们可以通过以下对比一下非泛型和泛型实现的区别。

generic1

图3非泛型和泛型类定义

      通过上图我们发现泛型类CustomStack和非泛型类CustomStack在实现上没有太大的区别,我们只需把具体类型换成T就OK了,接下来我们将进入泛型的进阶学习。

  • 泛型委托

     关于委托大家可以阅读《.NET 中的委托》和《.NET中的委托和事件(续)》,现在让我们学习一下泛型委托。

generic4

图4泛型委托的定义

      上图给出了泛型委托的定义,我们要注意Type parameters包括泛型参数和返回类型。随着C#3.0 Linq特性的引用,泛型委托的使用得到了极大地扩展,让我们通过具体代码讲讲如何使用泛型委托。

      首先我们定义一个泛型委托,然后再定义一个Calc类,接着在Calc里定义两个方法分别是Add() 和Divide() 如下:

/// <summary>
/// Define generic delegate.
/// </summary>
public delegate TR CalcMethod<T1, T2, TR>(T1 x, T2 y);public class Calc
{static private double sum;/// <summary>/// Gets or sets the sum./// </summary>/// <value>/// The sum./// </value>static public double Sum{get { return sum; }set { sum = value; }}/// <summary>/// Adds the specified x./// </summary>/// <param name="x">The x.</param>/// <param name="y">The y.</param>/// <returns></returns>static public string Add(int x , int y){Sum = x + y;return Sum.ToString();}/// <summary>/// Divides the specified x./// </summary>/// <param name="x">The x.</param>/// <param name="y">The y.</param>/// <returns></returns>static public string Divide(int x, int y){if (y == 0){return "除数不能为零";}Sum = x * 1.0 / y;return Sum.ToString();}} 

    接下来我们需要定义一个委托变量,而后将委托变量和具体调用方法绑定就OK了。

Initialize engeric delegate instance to Add method.
CalcMethod<int, int, string> calcMethod =new CalcMethod<int, int, string>(Calc.Add);
  • 类型约束

    前面例子我们并没有对泛型类型进行限制,由于T是一个抽象类型,有时我们必须限制T是哪种类型,例如我们可以定义CustomStack<int>,CustomStack<double>或CustomStack<string>类型的栈,但有时我们必须限制泛型类型。这时我们需要使用类型约束,引入关键字----where。

    C#中提供五种类型约束(类名,接口名,引用类型,值类型和构造函数),我们可以通过使用不同的类型约束来限定泛型类型。

类型约束

约束描述

ClassName

T只能是该类或继承该类的子类

InterfaceName

T只能是该接口或实行该接口的类型

struct

T是任意值类型

class

T是任意引用类型(如classes, arrays, delegates, and interfaces等)

new()

T是包含公有无参构成函数的任意类型

表1类型约束

 

      通过表1我们初步地了解不同类型约束之间的区别,现在让我们通过具体的例子来看看它们使用范围和区别。

      假设我们定义了一个泛型结构体struct RefSample<T> where T : class,现在考考大家以下例子符合类约束的有(提示值类型和引用类型区别):

 

A.  RefSample<IDisposable>

B.  RefSample<string>

C. RefSample<int[]>

D. RefSample<Guid>

E.  RefSample<int>

     激动人心的时刻又到了现在让我们快快公布答案吧!答案是ABC,因为该泛型的约束是引用类型约束,如果大家还不清楚请看一下引用类型约束的定义。

     通过类约束例子,我们举一反三值类型约束就是约束类型为值类型。

     OK接下来让我们介绍构造函数约束。在介绍构造函数约束之前,让我们回顾一下符合构造函数约束的条件:任意值类型,任意非静态非抽象无明确定义构造函数的类和任意非抽象有明确定义无参构造函数的类。这句话很别扭,让我们通过具体例子讲解一下。

 Define generic methodWhich has constructor type constraints
public T CreateInstance<T>() where T : new() 
{
return new T(); 
}CreateInstance<int>();
CreateInstance<object>();
CreateInstance<string>();

      现在我们定义了一个泛型方法并且添加构造函数约束,调用CreateInstance<int>() 和 CreateInstance<object>() 成功,但调用CreateInstance<string>() 失败,这由于String类没有提供无参构造函数。也许有人会问:“我们很难判断每种类型是否包含默认构无参造函数”,确确是这样但我们可以记住的是C#规定值类型提供默认无参构造函数。

 

  • 组合约束

     前面的泛型例子都是只使用一种类型约束,但有时候我们要使用多种类型约束,这时我们就可以使用组合约束,在使用约束的时我们要注意约束的先后次序,约束次序如下:

generic3

图5约束次序

     通过上图我们发现ClassName,class和struct是主要约束,接口名是第二约束,最后是构造函数约束。OK现在我们对组合约束有了初步地了解,那么让我们通过以下例子加深理解。

A. class Sample<T> where T : class, struct

B. class Sample<T> where T : Stream, class

C. class Sample<T> where T : new(), Stream

D. class Sample<T> where T : IDisposable, Stream

E. class Sample<T> where T : XmlReader, IComparable, IComparable

F. class Sample<T,U> where T : struct where U : class, T

G. class Sample<T,U> where T : Stream, U : IDisposable

     上面给出错误使用组合约束的例子(注:A~E单类型组合约束,FG多类型组合约束),请大家指出约束错误的原因,如果不清楚错误的原因大家回忆一下约束定义。

1.1.3 总结

     本文注意介绍了C# 中泛型的基础知识给出自己的总结,而且这不是C# 泛型的全部,如果大家在看完本文之后希望进一步学习泛型这才是我的目的。

泛型的优点:

  • 编译时进行类型检测,减少运行时异常InvalidCastException。
  • 泛型使用强数据类型。如我们实例一个CustomStack<CustomStruct>对象objStack,通过调用Push()方法成功把CustomStruct压入栈中,而objStack.Push(“Stack”)失败。
  • 值类型无需进行boxing和unboxing操作。例如前面的泛型Push()和Pop()方法存放和取出值类型时无需进行boxing和unboxing操作。
  • 减少代码量。如:我们定义一个泛型栈,就不用像前面例子那样分别定义int和string类型的栈。
  • 程序性能提高,因为无需类型转换,从而减少类型检测。
  • 使用泛型减少内存消耗。因为无需进行boxing操作,不用重新分配堆空间。
  • 代码可读性更强。

      很多人喜欢将C# ,C++ 和Java中的泛型进行对比,而且也有很多相关的讨论,在这里我也给出自己的想法,我觉得C++ 的泛型功能的确比C# 和Java都要强大。

Creative Commons License

[作者]:JK_Rush
[出处]:http://www.cnblogs.com/rush/
[本文基于]: 署名-非商业性使用 3.0 许可协议发布,欢迎转载,演绎,但是必须保留本文的署名 JK_Rush (包含链接),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

年底了,给想进阶的的前端朋友一些福利

2020 年&#xff0c;很多朋友都经历了一段比较艰难的求职季。年末&#xff0c;“就业寒冬”迎来了一丝暖阳&#xff0c;很多中大型互联网公司扩大了未来一年的招聘需求。前不久&#xff0c;字节跳动放出了年末要招 1 万人的消息&#xff0c;腾讯校招规模也将扩张至 5000 人&…

Vue Router 4.0 正式发布!焕然一新。

关注若川视野&#xff0c;回复"pdf" 领取资料&#xff0c;回复"加群"&#xff0c;可加群长期交流学习12月8日&#xff0c;Vue Router 4 正式发布稳定版本。在经历了 14 个 Alpha&#xff0c;13 个 Beta 和 6 个 RC 版本之后&#xff0c;Vue Router v4 闪亮…

实战Nginx与PHP(FastCGI)的安装、配置与优化

转载链接&#xff1a;http://ixdba.blog.51cto.com/2895551/806622 一、什么是 FastCGI FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI&#xff0c;包括Apache、Nginx和lighttpd等&#xff0c;同时&#xff0c;…

10个前端8个用Vue的,怎么才能在面试中出彩?

大家好&#xff0c;我是若川。现在但凡出去面试&#xff0c;面试官几乎必问 Vue3.0 。不仅会问一些核心特性&#xff0c;还会问原理层面的问题。比如&#xff1a;▶框架层面问题&#xff1a;Vue3.0 新特性 Composition API 与 React.js 中 Hooks 的异同点&#xff1f;▶源码、原…

故乡 | 登高望远,夜幕降临

欢迎星标我的公众号若川视野&#xff0c;回复加群&#xff0c;长期交流学习上周末看了几集豆瓣评分8.5分刘同同名小说的青春剧《我在未来等你》&#xff0c;让我回想起自己的高中生活。也想起小时候经常爬到故乡附近的小山&#xff0c;看夕阳西下。时常和同事开玩笑说&#xff…

CentOS5安装Nginx1.4+PHP5.5 FastCGI

转载链接&#xff1a;http://blog.csdn.net/staricqxyz/article/details/17012329 yum -y install gcc gcc-c autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2…

一份热乎乎的滴滴前端面经

关注若川视野&#xff0c;回复"pdf" 领取资料&#xff0c;回复"加群"&#xff0c;可加群长期交流学习滴滴前端实习面经滴滴是我投简历之后第二家面试的公司&#xff0c; 国庆节前两三天投的简历&#xff0c; 国庆后复工第一天就给我打了电话约一面。那时候…

用webBrowser取源文件取不到的点击数--选秀榜selectop.com网站内容管理系统之六

用idhttp可以取到源文件&#xff0c;但网站用脚本代码&#xff0c;源文件是看不到&#xff0c;并且代码的结果也取不出。webBrowser可以多次返回下载到的内容&#xff0c;不包括任何html语法&#xff0c;这个当中就有文章的点击数。在WebBrowser1DownloadComplete事件中处理&am…

真诚推荐几个最值得关注的前端公众号

前端技术日新月异&#xff0c;发展迅速&#xff0c;作为一个与时俱进的前端工程师&#xff0c;需要不断的学习。这里强烈推荐几个前端开发工程师必备的优质公众号&#xff0c;希望对你有所帮助。大家可以像我一样&#xff0c;利用碎片时间阅读这些公众号的文章。code秘密花园一…

Silverlight Unit Test Framework

微软在08年的时候推出了一个Silverlight的单元测试框架&#xff0c;该框架在Mix 08的时候与Silverlight controls同时推出的&#xff0c;微软工程师Jeff Wilcox一直参与维护该单元测试框架。Scott Gu对这个框架的介绍Jeff Wilcox提供的视频介绍该框架的源代码已经包括在Silverl…

Nginx 反向代理、负载均衡、页面缓存、URL重写及读写分离详解

转载链接&#xff1a;http://freeloda.blog.51cto.com/2033581/1288553 大纲 一、前言 二、环境准备 三、安装与配置Nginx 四、Nginx之反向代理 五、Nginx之负载均衡 六、Nginx之页面缓存 七、Nginx之URL重写 八、Nginx之读写分离 注&#xff0c;操作系统为 CentOS 6.4 x86_64…

能在任意一种框架中复用的组件,太牛了!

Web Component 是一种 W3C标准支持的组件化方案&#xff0c;通过它可以编写可复用的组件&#xff0c;同时也可以对自己的组件做更精细化的控制。更牛的是&#xff0c;Web Component 可以在任何一种框架中使用&#xff0c;不用加载任何模块、代码量小&#xff0c;优势非常明显&a…

stm32cubemx中文_用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本)

概述​ 在使用 DHT11 的时候&#xff0c;时序通信需要微秒来操作&#xff0c;STM32CubeMX 自带一个系统时钟&#xff0c;但是实现的是毫秒级别的。因此就自己用通用计时器实现一个。文章目录环境&#xff1a;开发板&#xff1a;STM32F4探索者&#xff08;正点原子&#xff09;1…

人脸识别拷勤门禁主板_捷易讲解AI无感人脸识别考勤门禁终端设备在使用中的维护方法...

人脸识别考勤门禁终端设备虽然在出厂时&#xff0c;都有做密封处理&#xff0c;但面对细小的灰尘&#xff0c;并没有做到百分百防尘。灰尘对于AI无感人脸识别考勤门禁终端设备是有一定的影响的&#xff0c;他会沉淀在主板上、屏幕上&#xff0c;影响设备散热和正常工作&#xf…

【翻译】How-To: Using the N* Stack, part 3

原文地址&#xff1a;http://jasondentler.com/blog/2009/08/how-to-using-the-n-stack-part-3/ Java – 一种代码松散的XML 在我们学习 Fluent NHibernate 之前, 应该先了解下老式的 NHibernate 映射文件应该是怎样写的。 在一个典型的 NHibernate 配置中&#xff0c;你会有很…

你可能需要的网易前端三轮面经

关注若川视野, 回复"pdf" 领取资料&#xff0c;回复"加群"&#xff0c;可加群长期交流前言最近一个星期面了几家公司&#xff0c;最后收获了心仪的网易有道offer&#xff0c;于是推掉了其他的面试&#xff0c;至于一些其他大厂&#xff0c;并没有投简历&am…

复习.net/c#时的小文章之万年草稿版 (全是基础概念,请懂的人绕行)

必读文&#xff1a;61条面向对象设计的经验原则&#xff08;体会篇&#xff09; C#知识点集合 (面试必备)一、显式(explicit)转换和隐式(implicit)转换的一般概念int i 100; Response.Write(i); // 这就是隐式 Response.Write(i.ToString()); // 这就是显式 一般来讲&#xff…

timertask run函数未执行_图执行模式下的 TensorFlow 2

文 / 李锡涵&#xff0c;Google Developers Expert本文节选自《简单粗暴 TensorFlow 2.0》尽管 TensorFlow 2 建议以即时执行模式(Eager Execution)作为主要执行模式&#xff0c;然而&#xff0c;图执行模式(Graph Execution)作为 TensorFlow 2 之前的主要执行模式&#xff0c…

如何从 0 到 1 打造团队 PC/H5 构建工具

关注若川视野, 回复"pdf" 领取资料&#xff0c;回复"加群"&#xff0c;可加群长期交流学习一、前言 大家好&#xff0c;我叫鳗鱼&#xff0c;这次分享的主题是如何从 0 到 1 打造适合自己的构建部署方案。image.png先例行的自我介绍&#xff0c;大概 14 年…

testng接口自动化测试_Java+Maven+TestNG接口(API)自动化测试教程(10) 使用 Jenkins 构建自动化测试持续集成...

现在代码可以运行了&#xff0c;但是每次运行都需要我们手工去执行&#xff0c;并且测试报告也只能在执行测试的电脑上才能看到&#xff0c;我们希望能够定时自动执行测试&#xff0c;并且能够做到自动发送测试报告到相关人员的电子邮箱中。Jenkins 正好可以很好的完成以上诉求…