当前位置: 首页 > 编程日记 > 正文

.NET泛型解析(下)

上一篇对.NET中的泛型进行了详细的介绍以及使用泛型的好处是什么,这篇将更加深入的去了解泛型的其他的知识点,重头戏.

【1】泛型方法

上一篇我们也说过了,泛型可以是类,结构,接口,在这些泛型类型中定义的方法都可以叫做泛型方法,都可以引用由泛型类型本身指定的一个类型参数例如:
1 public class GenericType<T>
2 {
3 
4      private T G_Value;
5 
6      public T Convert<T> { T res = (T)Convert.ChangeType(G_value,typeof(T)); return res; }
7 
8 }
泛型方法的存在为我们提供了极大的灵活性。
泛型方法顾名思义,首先它是一个方法,只不过这个方法的返回值,参数可能是泛型的,
类型推断
类型推断就是根据泛型方法中定义的形参类型T,在我们调用的使用,那么所要传的也应该是一致的。例如:
1 static void Test<T >(ref T t1,ref T t2)
2 {
3        T temp = t1;
4        t1 = t2;
5        t2 = temp;
6 }

这是我们定义的一个泛型方法,第一个参数类型为 T,第二个也为 T,说明这两个类型是一致的。那么我们在调用的时候应该这样 :

1   int i = 8, j = 0 ;
2 
3   Test( ref i, ref j);

而如果我们两个实参的类型不统一的话,那么就会引发编译异常例如 :

开放类型和封闭类型

具有泛型类型参数的类型对象让就还是一个类型, CLR内部依旧和其他的对象类型一样创建一个内部类型对象, 那么这种具有泛型类型参数的类型称之为开放类型,开发类型不能进行构造任何的实例.
当泛型类型参数是一个实参时,那么所有的类型实参传递的都是实际的数据类型了,这种类型称之为 封闭类型,封闭类型是允许构造实例对象的
 1  class DictionaryStringKey <T> : IEnumerable<T>
 2  {
 3        public IEnumerator<T> GetEnumerator()
 4        {
 5            throw new NotImplementedException();
 6        }
 7 
 8        System .Collections. IEnumerator System. Collections.IEnumerable .GetEnumerator()
 9        {
10            throw new NotImplementedException();
11        }
12  }
View Code

在上述代码中,有一个DictionaryStringKey的类,继承了一个IEnumable<T>的泛型,那么根据我们的开放类型它在调用的时候是不只是具体的实参,并且不能被构造实例的.

 1          static void Main( string[] args)
 2         {
 3             object o = null;
 4             Type t = typeof( DictionaryStringKey <>);
 5            o =   CreateInstance(t);
 6            Console .ReadLine();
 7 
 8         }
 9 
10         private static object CreateInstance( Type t)
11         {
12             object o = null;
13             try
14             {
15                 o = Activator . CreateInstance(t);
16                 Console .WriteLine( @" 创建了 " + t + "的实例 " , t .ToString());
17             }
18             catch (Exception ex)
19             {
20                 o = Activator . CreateInstance(t);
21                 Console .WriteLine( @" 创建 " + t + "的实例失败 " , t .ToString());
22             }
23           
24             return o;
25         }
View Code

上述代码中我尝试着对开放类型进行实例化,结果报出异常 :

当我们传入实参之后,那么它就是一个封闭类型,这样我们就可以进行构造实例了.

1   static void Main( string[] args)
2         {
3             object o = null;
4             Type t = typeof( DictionaryStringKey <string > );
5            o =   CreateInstance(t);
6            Console .ReadLine();
7 
8         }
View Code

 【2】泛型接口

泛型的主要作用就是定义泛型的引用类型和值类型,在CLR中,对于泛型接口的支持也是很重要的,因为这样更有益与程序的扩展性, 另外一点如果没有泛型接口我们每次使用非泛型接口时都会进行装箱操作(例如:  IComparable),并且会失去编译时的安全性(因为它是非泛型的,较之泛型的好处之一).

那么CLR提供了对于泛型接口的支持,对于引用类型和值类型可以通过制定实参的方式来实现泛型接口,同时也可以保持未指定状态来实现一个泛型接口( 接受者也可以是泛型 , 可以在后期进行指定类型)

实例 1 : 声明一个泛型接口

1      interface IPair <T>
2     {
3         T First { get; set ; }
4         T Second { get; set ; }
5     }
IPair这个接口实现了一对相关的对象,两个数据项具有相同的类型.
实现接口时,语法与非泛型类的语法是相同的,如果在实现泛型接口时,同时不指定实参,则默认为是一个泛型类.
实例 2 : 泛型接口的实现
 1      class Pair <T> : IPiar< T>
 2     {
 3         public T First
 4         {
 5             get
 6             {
 7                 throw new NotImplementedException();
 8             }
 9             set
10             {
11                 throw new NotImplementedException();
12             }
13         }
14 
15         public T Second
16         {
17             get
18             {
19                 throw new NotImplementedException();
20             }
21             set
22             {
23                 throw new NotImplementedException();
24             }
25         }
26     }
View Code

CLR泛型接口的支持对于集合类型来讲尤其重要,同时泛型在集合类型中较为常用,如果没有泛型那么我们所使用List的时候都要依赖与System.Collections,我们在每次访问的时候都需要执行一次转换,如果使用泛型接口,就可以避免执行转型,因为参数化的接口能实现更强的编译时绑定.

实例 3 : 在一个类中多次实现相同的接口

 1     interface IContainer <T>
 2    {
 3        ICollection<T > Items { get ; set; }
 4    }
 5 
 6     public class Person : IContainer<A >,IContainer <B>
 7     {
 8 
 9         public ICollection <A> Items
10         {
11             get
12             {
13                 throw new NotImplementedException();
14             }
15             set
16             {
17                 throw new NotImplementedException();
18             }
19         }
20 
21         ICollection<B > IContainer <B>. Items
22         {
23             get
24             {
25                 throw new NotImplementedException();
26             }
27             set
28             {
29                 throw new NotImplementedException();
30             }
31         }
32     }
View Code

Items属性使用一个显式接口实现多次出现,每一次,类型参数都有所不同,如果没有泛型,这是做不到的,在非泛型的情况下,CLR只允许一个显式的IContainer.Items属性.

【4】泛型约束(主要约束,次要约束,构造器约束)

约束也可以理解为是一种规则,意思就是我们所编写的所有泛型,你在调用的时候,应该给我传输那些类型的实参。

在上一篇中,我们也在一个DataTableToList中使用到了约束,但是并没有进行讲解,在这里我们详细的说明一下:
在.NET 中有4中约束方式,它们的常规语法是相同的,约束要放在泛型方法或者泛型类型声明的末尾,并由上下文关键字 ”where :“  来引入 。
主要约束(引用类型约束):
主要约束表示的是 : 我们在指定一个引用类型约束时,那么一个指定的类型实参要么是与约束类型相同的类型,要么是从约束类型派生的一个类型
实例 4 : 引用类型约束
1 struct Generic<T>(T t1) where : class
这里我定义了一个泛型方法,Generic,后来写了一个where : class,这就告诉编译器,在使用者调用这个方法时,必须为这个方法的传入一个引用类型的实参,同时,我们也可以注意到这个方法的返回类型为struct值类型。
主要约束(值类型约束)
很明显,值类型约束的话,就是将 约束条件指定为 Struct
实例 5 : 值类型约束
1  class ConstraintOfStruct <T> where T : struct
2  {
3      public T result()
4      {
5          return new T();
6      }
7  }

在代码中,我们最后返回的是new T(),这个是没有问题的,因为我们已经可以肯定T就是值类型,所有的值类型都有一个隐式的构造函数,那么如果我们将 约束条件改为 class的话,那么就会出异常,因为不是所有的引用类型都可以实例化.

实例 6 : 反面教材 - 约束为引用类型时进行实例化.

构造函数类型约束
构造函数类型约束表示为 T : new(),这个约束必须为所有的类型参数的最后一个约束,它用于检查类型实参是否有一个可用于创建类型实例的无参构造函数,这一点比较适用于所有的值类型,所有的没有显式声明构造函数的非静态、非抽象类,所有显式声明了一个公共无参构造函数的非抽象类。
实例 7 : 构造函数类型约束
1   public T CreateInatance<T >() where T : new()
2   {
3       return new T();
4   }

在方法createInstance中,要求我们必须传入一个无参的构造函数,例如:我们可以传入 object,struct,stream等,但是不能传入 string,Directory等.

接口约束:

接口约束的一个好处就是我们可以指定多个接口,但是只能指定一个类
1     class ConstraintOfStruct <T> where T : class,IEquatable <T> ,IComparable<T>
2     {
3     }
组合约束 :
组合约束就是指定多个约束条件在上一个约束中我们已经看到了 ,更详细的如下 :
1     class Sample<T> where T : class ,IDisposable,new(){}
2     class Sample<T> where T : struct,IDisposable{}
3     class Sample<T,U> where T : Stream where U : IDisposable{}
4  
5     class Sample<T> where T : class ,struct
View Code

【5】泛型类型转型

1  public static T Desercialies <T > (Stream stream, IFormatter formatter)
2 {
3       return (T)formatter. Deserialize(stream);
4 }

formatter 负责将流转换为 Object,返回一个Object类型,我们在使用泛型调用的时候应该这样来 :

1   string result = Desercialies <string > (stream, formatter);

上述的代码看着是一种强制转换,但是仍然执行了隐式的转换,就和使用非泛型 string result = (string)Desercialies(stream, formatter); 一样

【6】泛型和属性

属性也可以应用到泛型类型中,使用的方式和非泛型类型相同.自定义的属性只允许引用开发泛型类型

1     class MyClass<T,S> where T :class 
2           where S : struct
3     {
4         public T MyName { get; set; }
5         public S MyCode { get; set; }
6         public List<T> MyCourse { get; set; }
7         public List<S> MyStudyHistory { get; set; }
8     }

上述我们定义了一个MyClass 类,这个类有两个参数一个是引用类型一个是值类型.在属性MyName,MyCourse的类型都是引用类型,MyCode的类型都为值类型

泛型类型构造器:

 1      class Pair < T> : IPiar< T >
 2     {
 3         private T _first;
 4 
 5         public T First
 6         {
 7             get { return _first; }
 8             set { _first = value; }
 9         }
10         private T _second;
11 
12         public T Second
13         {
14             get { return _second; }
15             set { _second = value; }
16         }
17         public Pair(T first,T second)
18         {
19 
20         }
21     }
22     interface IPiar < T> { }
View Code

泛型的构造器不需要添加类型参数来与类的声明一致.

 

先到这里, 下一篇着重分享协变和逆变,否则这一篇符太长,不利于以后的查看.

泛型委托和泛型反射留在深入总结委托和反射的时候进行总结。

同时,希望给在阅读的你带来一些帮助。

转载于:https://www.cnblogs.com/DeepLearing/p/4564791.html

相关文章:

spark集群使用hanlp进行分布式分词操作说明

本篇分享一个使用hanlp分词的操作小案例&#xff0c;即在spark集群中使用hanlp完成分布式分词的操作&#xff0c;文章整理自【qq_33872191】的博客&#xff0c;感谢分享&#xff01;以下为全文&#xff1a;分两步&#xff1a;第一步&#xff1a;实现hankcs.hanlp/corpus.io.IIO…

让VirtualBox的虚拟机器在电脑开机时自动启动

当你安装很多套Virtualbox的虚拟机器系统后&#xff0c;希望能在开机后自动启动虚拟机器的系统。 Linux (Host OS):在你的/etc/rc.local中加入下列几行VBoxVRDP -startvm WinXP & VBoxVRDP -startvm Win2003 & VBoxVRDP -startvm LinuxFC6 & Windows (Host OS): 开…

L1-025 正整数A+B

不确定的点&#xff1a; 1.数据用什么类型输入&#xff0c;如果用字符串类型输入&#xff0c;怎么判断它是不是正整数 2.怎么判断哪部分是A&#xff0c;哪部分是B 解析 c语言’\0’ 意思&#xff1a; 字符常量占一个字节的内存空间。字符串常量占的内存字节数等于字符串中字节…

制作显示欢迎信息的脚本程序

终端程序恐怕是Linux用户使用最为频繁的了。我的Debian系统启动后便是直接进入的终端界面。为了在每次登录时或者是在X视窗环境下打开终端程序时显示一些欢迎信息&#xff0c;比如当前的日期、名人警句等&#xff0c;从而可以增加一些生活情趣&#xff0c;就可以创建一个脚本程…

文章分页浏览(二)

分页的方法: View Code publicstringOutputBySize(stringp_strContent, stringbType) { stringm_strRet ""; intm_intPageSize 500;//文章每页大小 intm_intCurrentPage 1;//设置第一页为初始页 intm_intTotalPage 0; intm_intArticlelengt…

云计算时代,如何选择适合自己的云服务器厂商?

据百科定义&#xff0c;云服务器是一种处理能力可弹性伸缩的计算服务&#xff0c;帮助您快速构建更稳定、安全的应用&#xff0c;降低开发运维的难度和整体IT成本&#xff0c;使您能够更专注于核心业务的创新。云服务器相对传统服务器有些优势&#xff1a;按国内市场上云服务器…

解决mysql建立的数据库名字不能带大写字母

1、在安装目录下&#xff0c;找到 my.ini文件 2、找到 [mysqId]节点 3、在它下面添加 lower_case_table_names2如果设置为0的话&#xff0c;我的Mysql服务不能重启 4、重启Mysql 直接手动重启即可&#xff0c;在服务中&#xff0c;找到Mysql服务&#xff0c;先停止&#xff0c…

CSS position财产

CSS在position位置信息要素用于表示属性。 有三个起飞值&#xff1a;static, absolute, relative。假设元件不显式配置position财产&#xff0c;该元素默认position 值至static。 1、static&#xff1a;这是表示该元素依照排列和嵌套的顺序和规则应该在的位置&#xff0c;此时设…

通过产品ID得到collection!!!

You can do as following for filtering products id 10 and 12$products->addAttributeToFilter(entity_id, array(in>array(10,12)));当然这种方式的上面&#xff0c;必须用 ->addAttributeToSelect();下面这种方式比较实用的&#xff0c;如果想按照名字排序&#…

jupyter notebook出现cannot import name 'create_prompt_application'问题(Died Kernel)

应该是在安装其它python第三方库时更新了prompt-toolkit版本&#xff0c;降级到下面的版本即可&#xff1a; sudo pip install prompt-toolkit1.0.15 转载于:https://www.cnblogs.com/darklights/p/10302706.html

导入sql时出现Invalid default value for ‘create_time‘报错处理方法

&#xff08;上图是初始的sql文件的内容&#xff09; 在开发微信小程序时&#xff0c;需要导入.sql文件&#xff0c;但是最一开始导入的时候没有任何改动进行了导入&#xff0c;报错如下 PS E:\weichatApp\my-project\server> node tools/initdb.js 开始初始化数据库... 准…

Python相关机器学习

Python机器学习库 Python的机器学习库汇总与梳理 机器学习之开源库大总结 转载于:https://www.cnblogs.com/SFMing/p/4590261.html

Django 图片上传upload_to路径指定失效的问题记录

为什么80%的码农都做不了架构师&#xff1f;>>> 初始方法一&#xff1a; 疑虑&#xff1a;model使用upload_to自定义路径方法失效&#xff0c;指定路径也失效。最后以Views中指定MEDIA_URL和MEDIA_ROOT做拼接&#xff0c;并且自行判断并建立文件夹&#xff0c;手动…

javascript tab切换类LixTabs最新版

javascript Tab切换类LixTabs&#xff0c;更新至0.5版: 受snandy的“读jquery”系列的启发&#xff0c;改进了代码&#xff0c;现在调用LixTabs时不用加new了。即可以这样写&#xff1a;var tab Tabs();把原来的参数evt&#xff0c;改成了易理解的event(我的疏忽)总代码量&…

linux虚拟机文件挂载

把U盘中的文件上传到linux虚拟机中&#xff0c;可以采用挂载的方式。 &#xff08;1&#xff09;启动虚拟机 &#xff08;2&#xff09;把U盘插入电脑中 &#xff08;3&#xff09;输入命令 fdisk -l可以查看新的分区 &#xff08;4&#xff09; cd /mnt mkdir usb mount /d…

HDU 1757 A Simple Math Problem

Problem Description Lele now is thinking about a simple function f(x).If x < 10 f(x) x.If x > 10 f(x) a0 * f(x-1) a1 * f(x-2) a2 * f(x-3) …… a9 * f(x-10);And ai(0<i<9) can only be 0 or 1 .Now, I will give a0 ~ a9 and two positive intege…

mariadb 内存占用优化

本文由云社区发表 作者&#xff1a;工程师小熊 摘要&#xff1a;我们在使用mariadb的时候发现有时候不能启动起来&#xff0c;在使用过程中mariadb占用的内存很大&#xff0c;在这里学习下mariadb与内存相关的配置项&#xff0c;对mariadb进行调优。 查询最高内存占用 使用以下…

windows程序设计之对话框简介1

这里先介绍下wParam和lParam&#xff0c;对于鼠标而言&#xff0c;LOWORD(wParam)和HIWORD(wParam)代表鼠标位置x,y坐标&#xff0c;对于菜单和控件而言&#xff0c;两者wParam的低字节都是各自的ID&#xff0c;即LOWORD(wParam)都是ID。两者的高字节对菜单而言是0&#xff0c;…

linux虚拟机下安装Tomcat

&#xff08;1&#xff09;首先通过挂载的方式把 tomcat的安装包从U盘上传到虚拟机中 我上传的路径是 &#xff1a;usr/tomcat &#xff08;2&#xff09; cd /usr/tomcat tar xzvf 压缩包的名字 ##进行解压&#xff08;3&#xff09;进到tomcat安装目录下的bin文件夹 ./…

unity中使用自定义shader进行光照贴图烘培无法出现透明度的坑爹问题

最近开发中在对场景进行光照贴图烘焙时发现一个坑爹问题&#xff0c;在使用自定义shader的时候&#xff0c;shader命名中必须包含Transparent路径&#xff0c;否则烘焙的时候不对alpha通道进行计算&#xff0c;烘焙出来都是狗皮膏药 比如一个shader叫 Shader "xx/UnlitAlp…

动软代码生成器教程——懒人有福了

很多时候项目必须是三层架构模式&#xff0c;但是很多繁琐的代码让多数程序员闹心……那有没有一个省时省力的工具快速的帮我们搞定三层架构呢&#xff1f;回答是肯定的&#xff0c;很早之前技术牛人李天平就开发出了这么一款工具&#xff0c;目前该工具还在不断的更新&#xf…

unity3d做简单小游戏可以吗?

可以吗&#xff1f;当然。如果是独立开发&#xff0c;主要在美工&#xff0c;这类的游戏程序简单&#xff0c;有些基础就行&#xff0c;美工要做得好可不容易&#xff0c;要是效果要求不高&#xff0c;随便在max拉几个模型吧。unity方面&#xff0c;熟悉一下&#xff0c;如果有…

逻辑覆盖测试(一)语句覆盖

语句覆盖&#xff1a; 设计测试用例时保证程序的每条语句至少执行一次。 简单来说&#xff0c;就是每个语句都覆盖一遍。 例子&#xff1a; 流程图如下&#xff1a; 测试用例如下&#xff1a; x4,z9&#xff0c;第一个if语句执行到了&#xff1b; x4,y7,第二个if语句为true…

「小程序JAVA实战」小程序的视频展示页面初始化(63)

转自&#xff1a;https://idig8.com/2018/09/24/xiaochengxujavashizhanxiaochengxudeshipinzhanshiyemianchushihua62/ 进入列表详情&#xff0c;展示点赞状态用户的名称&#xff0c;头像名称。源码&#xff1a;https://github.com/limingios/wxProgram.git 中No.15和springbo…

.NET判断字符串是否是数值型或xxx型

using System.Text.RegularExpressions; Regex digitregex new Regex("^[0-9]\d*[.]?\d*$"); if (!digitregex.IsMatch(TextBox1.Text)) { TextBox1.Text""; MessageBox.Show("只能输入数字&#xff01;","提示…

Spring.net使用说明

使用方法&#xff1a;1.在配置文件设置Spring.net 节点在配置节中&#xff0c;声明Spring.net&#xff0c;配置 context&#xff0c;objects 标签&#xff0c;来源&#xff08;type&#xff09;<!--配置节&#xff1a;主要用来 配置 asp.net框架之外的 标签&#xff0c;告诉…

逻辑覆盖测试(三)条件覆盖

条件覆盖&#xff1a;设计测试用例时应保证程序中每个复合判定表达式中&#xff0c;每个简单判定条件的取真和取假情况至少执行一次。 例子&#xff1a; 流程图&#xff1a; 测试用例&#xff1a; 程序中一共两个if语句&#xff0c;都是复合判定条件&#xff0c;其中的简单…

Linux UserSpace Back-Door、Rootkit SSH/PAM Backdoor Attack And Defensive Tchnology

catalog 0. 引言 1. Pam后门 2. SSH后门 3. Hijacking SSH 4. Hijacking SSH By Setup A Tunnel Which Allows Multiple Sessions Over The Same SSH Connection Without Re-Authentication 5. Hijacking Active SSH Screen Sessions 0. 引言 0x1: 安全攻防观点 1. Know Your …

澳大利亚多地热浪来袭 最高温度超40摄氏度

中新网1月24日电 据澳洲网报道&#xff0c;近日&#xff0c;澳大利亚多地热浪来袭&#xff0c;其中&#xff0c;南澳和维州的部分地区气温将飙升至40摄氏度以上。维州政府发布声明&#xff0c;提醒民众做好应对高温天气的准备。资料图&#xff1a;当地时间1月21日&#xff0c;澳…

Multithread 之 introduction

Why multithreading?&#xff08;摘自《win32 多线程程序设计》&#xff09;单线程程序就像超级市场中唯一的一位出纳&#xff0c;这个出纳对于小量采购可以快速结账。但如果有人采购了一大车货品&#xff0c;结账就需要点时间了&#xff0c;其他每个人都必须等待。多线程程序…