.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 人&…

python oa系统_用python把C#操作OA的例子重写了一下

#手工chrome.exe --remote-debugging-port9222 --user-data-dir"C:\selenum\AutomationProfile"fromselenium import webdriverfromselenium.webdriver.common.by import Byfromselenium.webdriver.support.ui import WebDriverWaitfromselenium.webdriver.chrome.op…

编译安装PHP出现configure: error: MySQL configure failed. Please check config.log的解决方法

以下为google的结果&#xff1a; 方案一&#xff1a; 转载链接&#xff1a;http://www.php-oa.com/2008/03/28/php-make.html 好久没有编译安装过php了,为了玩nginx.没法子,编译一次来测试.我加的编译的参数是: # ./configure –prefix/usr/local/php –with-config-file…

[Android] Android学习手记(二)

1。SDK源码获取Git这个版本控制还真是第一次听到。Linux参考官网&#xff08;需要穿墙&#xff09;的Get source好像比较容易。Windows就比较麻烦了&#xff0c;不能通过repo方式获取整个projects&#xff0c;只能一个获取project。不过官网称“The source is approximentely 2…

关于分区索引与全局索引性能比较的示例

说明&#xff1a;之前使用range分区做出来的效果不明显&#xff0c;这次使用hash分区。 1、准备工作&#xff1a; ----创建两张一样的hash分区表&#xff0c;jacks_part和echos_part------------------ 1 SQL> create table jacks_part (owner varchar2(30),object_id numbe…

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;…

python在运维自动化的前景_现在学运维自动化python和大数据?

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":7,"count":7}]},"card":[{"des":"阿里云实时计算(Alibaba Cloud Realtime Com…

BOM算最尾阶的损耗率 成品直接到料件

假设由B生产为A经过3道工序,各工序的损耗率分别为 C1,C2,C3; 由D生产为B经过1道工序,作业损耗率为C4. 请问在BOM中建立材料的损耗率应该是怎样的呀? 我的理解是这样:A的产出B的投入(1-C1)(1-C2)(1-C3)所以B的投入A的产出/(1-C1)(1-C2)(1-C3)所以建A的BOM时,材料B的损耗率为: …

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

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

ASP.NET MVC学习之(5):Html.ActionLink

本文整理了该方法的几种重载形式&#xff1a; 一 Html.ActionLink("linkText","actionName") 该重载的第一个参数是该链接要显示的文字&#xff0c;第二个参数是对应的控制器的方法&#xff0c;默认控制器为当前页面的控制器&#xff0c;如果当前页面的控制…

python qq模块_常用的Python模块

目录1、使用copy模块来复制>>> class Animal:def _init_(self, species, number_of_legs, color):self.species speciesself.number_of_legs number_of_legsself.color color>>> harry Animal()>>> harry._init_(hippogriff, 6, pink)>>&…

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

欢迎星标我的公众号若川视野&#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…

FTP服务器的搭建

IIS IIS所提供的FTP功能比较简单&#xff1a; 用户依赖于“操作系统用户”&#xff1b;只提供了全局读&#xff08;浏览和复制&#xff09;、写&#xff08;删除、修改、添加&#xff09;功能设置&#xff0c;也就是说所有的读写权限都相同&#xff1b;“用户”与“对应目录”的…

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

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

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

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

Nginx负载均衡配置

转载链接&#xff1a;http://blog.csdn.net/staricqxyz/article/details/16984029 将域名指向Nginx服务器 访问www.test.com会转发到192.168.1.22,192.168.1.23 user nobody nobody; worker_processes 1; events { worker_connections 1024; } http { …

linux查看python环境变量_Linux中添加PYTHONPATH配置anaconda环境变量方法

因为最近开发多智能体模型需要把自己写的环境打包import&#xff0c;环境是统一的&#xff0c;如果不加入环境变量&#xff0c;每次测一个算法都要把包作为附属脚本和算法脚本放一起非常麻烦。所以就想把这些写的环境加入到python的环境变量里&#xff0c;这样就不用每次测试都…

yii_wiki_145_yii-cjuidialog-for-create-new-model (通过CJuiDialog来创建新的Model)

/**** CJuiDialog for create new model http://www.yiiframework.com/wiki/145/cjuidialog-for-create-new-model/translated by php攻城师http://blog.csdn.net/phpgcsIntroduction Scenario Preparation of the form Enhance the action create The dialog Summary ***/Intr…