看完这篇,终于知道自己会不会 C# 泛型了!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://csdnnews.blog.csdn.net/article/details/97845716

640?wx_fmt=gif

640?wx_fmt=jpeg

作者 | 羽生结弦

责编 | 胡巍巍

在开发过程中,同一段代码处出现多次调用,并且会有不同的类型在使用,这种就叫做跨类型代码复用。一般情况下跨类型代码复用我们会用到如下两种方法:

1. 继承;

2. 泛型。

继承是通过父类来代表代码复用,而泛型是通过带有占位符的模板来代码复用,其中占位符指的是类型。

比如:int、syting和实体等。本片主要讲解泛型的相关知识,下面就来详细讲解一下泛型。
 

640?wx_fmt=png

零、泛型类型


泛型会声明类型参数,消费者需要提供类型参数来把占位符类型填充上。我们先来看一个例子:


 

在上面的代码中当我们将string传入泛型类中,将会隐式动态的创建类型,这种操作被称为合成,合成将发生在运行时,而非编译时。当我们在代码中传入非string类型的值时,在编译时将报错。同样在上面的代码中我们也看到了int传入泛型类中的情况,这就说明泛型类可以跨类型复用。

小知识1:

我们将GenericClass<T>称为开放类型(OpenType),而将GenericClass<string>称为封闭类型(CloseType),开放类型在编译后就变成了封闭类型,在运行时所有的泛型类型都是封闭类型,因为占位符已经被具体类型填充完毕。

小知识2:

对于每一种封闭类型,静态数据都是唯一的,例如:


 

上面代码中,MyClass中存在一个静态字段Count,我们看到前两次的调用输出的分别是1和2,但是第三次输出的确实1,那么这是为什么呢,原因就是前两次的类型参数和泛型类型种的静态字段的类型一致,而第三次的类型参数不一致导致的。

在泛型类型饿子类中可以继续让父类参数保持开放,也剋及关闭父类的类型参数,同样子类也可以引入新的类型,我们来看一下例子:


 

小知识:

封闭参数类型的时候,该类型可以把自己作为具体的类型,例子如下:


 

 

640?wx_fmt=png

泛型方法


泛型方法在方法签名内声明类型参数,例如下:


 

在上面的代码中我们看到 ```generic.GenericFun<int>(12,4);``` ,我们将int传入泛型方法中,现在我们将这行代码改写成如下形式 ```generic.GenericFun(12,4);```。

我们发现现在这段代码和前面那段代码缺少了 <int> ,那么这么写代码是否有错呢?

答案是要看情况,当编译器可以推断出参数类型的话,我们可以省略掉参数类型,但是当编译器无法推断出参数类型的话我们就必须写上参数类型了。

当然上面这段代码是可以正确运行的,因为编译器可以正确的推断出参数类型。
我们还有如下几点需要注意的:

1. 泛型类中的方法,如果方法引入了参数类型,那它就是泛型方法,反之就不是泛型方法;

2. 除了class、struct、interface、delegate 和方法可以引入类型参数外,属性、字段、索引器、事件和构造函数等都不能声明类型参数,但是可以使用所在泛型类的类型参数。

小知识:

泛型类型和泛型方法可以有多个参数类型,例如 ```class a<T,U>``` 调用方法和单个参数类型一样。结合这一点我们就可以推断出泛型类型和泛型方法可以出现重载,只要参数类型的数量不同就没问题。

在一些情况下我们需要获取参数类型的默认值,这时我们就可以使用default(T)来获得。
 

640?wx_fmt=png

约束

 

我们虽然可以使用所有类型作为泛型类型参数,但是我们在实际开发时很少会这么使用,一般会将类型参数约束到指定的范围内。泛型可用约束如下:

1. base-class:某一个父类的子类;

2. interface:必须是实现了指定的接口;

3. class:必须是引用类型;

4. struct:必须是非空值类型;

5. new():必须包含无参构造函数;

6. U:T:U必须继承T

我们使用的时候是这样的:


 

上面的代码表示 Generic1 类的参数类型T继承自Aclass,Generic2 类的T继承子Aclass,并且实现了 Binterface 接口,而且U包含了无参构造函数。

注意:约束可以用于泛型类型和泛型方法
 

640?wx_fmt=png

类型参数与转换


C#种转换支持如下几种:

1. 数值转换;

2. 引用转换;

3. 装箱拆箱转换;

4. 自定义转换。

在发生编译的时候,会根据一直类型的操作数来决定采用那种转换,但是在泛型中我们不知道具体的类型是什么,编译器就会默认使用自定义转换,那么就有可能出现错误。

为了解决这个问题,我们引入了as ,例如我们将传进来的值转换成StringBuilder,这时我们可以这么做:


 

 

640?wx_fmt=png

variance 转换

 

讲解variance 转换前,先来简单了解一下协变、逆变和不变。

1. 协变(Covariance):当T作为返回值输出的时候;

2. 逆变(Contravariance):当T作为输出值的时候;

3. 不变(Invariance):当T即是输入又是输出时。

注意:以上这三种就是variance,只能用在接口和委托中。

所谓variance 转换就是上面三种之间的相互转换,variance 转换是引用转换的一个方法,从a转换到b如果是本体转换或者隐式引用转换,那么就是正确的。例如:


 

作者简介:朱钢,笔名羽生结弦,CSDN博客专家,.NET高级开发工程师,7年一线开发经验,参与过电子政务系统和AI客服系统的开发,以及互联网招聘网站的架构设计,目前就职于北京恒创融慧科技发展有限公司,从事企业级安全监控系统的开发。

人工智能学习路线+实战训练

https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

【END】

640?wx_fmt=jpeg

 热 文 推 荐 

华人学者解开计算机领域 30 年难题:布尔函数敏感度猜想

百度移动:静悄悄的战争

三十年软件开发之路:老码农的自我修养!

☞Android 告急!

3个核心差异, 告诉你为什么Libra永远成不了比特币!

微博宕机复盘:什么样的技术架构,可支持80个明星并发出轨?

☞公开课 | 详解事件抽取与事件图谱构建

超酷炫!Facebook用深度学习和弱监督学习绘制全球精准道路图

中国第一程序员,微软得不到他就要毁了他!

640?wx_fmt=gif点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。

640?wx_fmt=png你点的每个“在看”,我都认真当成了喜欢

展开阅读全文

没有更多推荐了,返回首页