《C#高级编程》【第五章】泛型 -- 学习笔记



泛型是高级程序设计语言的一种特性。泛型的引入使得程序开发的效率得到提高,代码的重用性大大的提升。有了泛型,我们可以创建独立于被包含类型的类和方法,我们不必给不同的类编写功能相同的很多方法或者类,只创建一个方法或类就可以了。现在我们看看泛型的优点

性能上,泛型不需要进行类型转换(也就是拆箱和装箱)。

类型安全,和Object类相比,Object类属于非类型安全的,而泛型使用泛型类型,可以根据需要用特定的类型替换泛型类型,这样就保证了类型安全类。在编译时,只有与泛型类型T定义的允许使用的类型不同,编译器就会报错,这样我们就可以尽早的发现错误。

二进制代码重用,在C#中可以一次定义多次使用,然而C++却要不断的访问源码。

代码的扩展,对于引用类型,他们将会共享本地类的所有相同代码。只有值类型,才会每次实例化一个新类。

我们看完了泛型的优点,我们现在可以来看看泛型具体怎么使用。我们先看一幅图,了解一下泛型的组成。

1、泛型类

我们先看看泛型类型的命名方法:<1>一大写字母T开头 <2>如果泛型类型没有特定的要求,或者使用了两个、两个以上的泛型类型,就需要给出描述性的名称。(如:TValue,TKey等等)。

现在我们看看泛型类的声明语法:

[访问权限修饰符] class 类名<T>
{
//类体
}

类名的T就是泛型类型,具体类型实例化时给出。然而在类体中就可以直接将T当成一个具体的类型来使用。每个处理对象类型的类都可以有泛型实现方式。如果类使用了层次结构,那么泛型就可以很好的消除类型转换的操作。

注意:T两边的’<’和’>’不能少。

在创建泛型类时,我们还需要考虑一些问题。首先我们应该怎么初始化成员变量呢?因为我们并不知道究竟是引用类型还是值类型,为了解决这个问题我们就引入了default关键字。语法如下:

T value = default(T)

这样我们就圆满的解决成员变量初始化的问题。default会给值类型赋值为0,引用类型赋值为null。

如果泛型类需要调用泛型类型的方法,那么就必须添加约束。那么什么是约束呢?约束就是让泛型类型只能使用我们所约束的类型。我们使用where关键字来声明约束。公有以下6种约束:

<1>where T : struct 结构约束,类型T必须为值类型

<2>where T : class 引用约束,类型T必须是引用类型

<3>where T : IFoo 接口约束,类型T必须实现接口IFoo

<4>where T : Foo 类约束,类型T必须派生自基类Foo

<5>where T : new() 类型T必须有一个默认的构造函数

<6>where T1 : T2 裸型约束,类型T1派生自泛型T2。

对于裸型约束,我们来具体说说吧。我们举个例子:

public class Test<T1, T2> where T1 : T2
{
//类体
}

我们重点看看实例化的时候

var MyTest = new Test<Class1, Class2>();  

那么此时,Class1类就必须派生自Class2类,否则编译器就会报错。

使用泛型类型还可以组合多个约束,例如:

public class MyClass<T> where T : IFoo, new()
{
//类体
}

泛型类,既然作为类那么它必然可以继承。泛型类可以实现泛型接口,也可以派生自泛型基类。例:

public class Myclass<T> : IDD<T>
{
//类体
}

或者这样

public class Base<T>
{
}
public class MySub<T>: Base<T>
{
}

也可以是这样:

public class MySub<T>: Base<string>
{
}

派生类可以是泛型类还可以是非泛型类。

public classMysub: Base<int>
{
}

泛型类,比较特别的应该就是它的静态成员了。因为泛型类的静态成员只能在类的一个实例中共享。假设类MySub<T>存在静态字段x,那么如果同时对一个int型和string型使用了Mysub<T>类,所以就存在两组字段:Mysub<int>.x和MySub<string>.x

2、泛型接口

看到泛型接口,我们就要提到,抗变与协变。现在我们就看看什么抗变与协变。

假设:TSub是TParent的子类。

协变:如果一个泛型接口IFoo<T>,IFoo<TSub>可以转换为IFoo<TParent>的话,我们称这个过程为协变,IFoo支持对参数T的协变。

逆变:如果一个泛型接口IFoo<T>,IFoo<TParent>可以转换为IFoo<TSub>的话,我们称这个过程为逆变,IFoo支持对参数T的逆变。

泛型类型的协变用关键字out声明,泛型接口就是协变的,这就意味着其返回类型只能是T。

public interface IIndex<out T>
{
	T this[int index] {get ;}
}

泛型类型的抗变用关键字in声明,泛型接口就是抗变的。这样泛型类型T就只能作为方法输入。例如:

public interface IDisplay<in T>
{
	void Show(T test);
}

通常只有具备继承关系的对象才可以发生隐式类型转换,如Base b=new sub()。

协变和逆变可以使得更多的类型之间能够实现隐式类型转换、类型安全性得到保障。

3、泛型结构

其实泛型结构和泛型类几乎是一致的,只是泛型结构没有继承的特性。.Net平台提供的一个泛型结构是(可空类型)Nullable<T>。可空类型的引入,主要是为了解决数据库语言中的数字与编程语言中的数字的区别。虽然Nullable<T>使用过于的繁琐,于是我们就引入了一种特殊的语法,使用‘?’运算符。例:

int? x1;
Nullable<int> x2;

在上面的例子中,x1与x2这两种方式定义是等价的。

非空类型可以转化为可空类型。(总是成功的且可以隐式转化)

可空类型可以转化为非空类型。当可空类型的值为null时就会抛出异常。(需要显示转化)

如果不进行显示转化,我们就可以使用”??”运算符。如下:

int? x1 = GetNullableType();
int y1 = x1 ?? 0;

这样的话,当x1为null时,就会赋给y1一个0。

4、泛型方法

除了可以定义泛型类,泛型也是可以定义成方法的。语法如下:

[返回值类型] 方法名称<T>(参数列表)
{
}

其余的用法就和普通方法是一样的,唯一的区别就是它有一个通用类型T。

泛型方法和类一样,也可以加约束,具体约束的方法和泛型类是一样的。语法如下:

[返回值类型] 方法名称<T>(参数列表) where T : [约束方式]
{
}

以上就是泛型的内容了

时间: 2024-12-25 21:13:50

《C#高级编程》【第五章】泛型 -- 学习笔记的相关文章

C#高级编程 25.5 System.Transactions学习笔记

在.NET 1.x中,基本上是通过ADO.NET实现对不同数据库访问的事务..NET 2.0增加了System.Transactions名称空间,为.NET应用程序带来了一个新的事务变成模型. 所有的事务组件或者类型均定义在System.Transactions程序集中的System.Transactions命名空间下,我们直接称基于此的事务为System.Transactions事务. System.Transactions事务变成模型使我们可以显式(通过System.Transactions

[python核心编程] 第五章练习题

第五章 数字 5-2. 操作符,写一个函数,计算并返回两个数的乘积“整理的时候才看到我把题目看成两个数的和了” 5-3. 标准类型操作符.写一段脚本,输入一个测验成绩,根据下面的标准,输出他的评分成绩(A-F) A:90~100 B:80~89 C:70~79 D:60~69 F:<60 5-4. 取余.判断给定年份是否是闰年.使用下面的公式. 一个闰年后就是指他可以被4整除,但不能被100整除,或者它可以被400整除. [python核心编程] 第五章练习题,布布扣,bubuko.com

Python核心编程第五章习题

Python核心编程-第五章-习题 5.1  整形,讲讲Python普通整形与长整形的区别? Python的标准整形类型是最通用的数字类型.在大多数32位机器上,标准整形类型的取值范围是-2**32-2**32 - 1. Python的长整型类型能表达的数值仅仅与你的机器支持的(虚拟)内存大小有关,换句话说,Python能轻松表达很大的整数. 长整型类型是标准整形类型的超集,当程序需要使用比标准整形更大的整型时,可以使用长整型类型,在整型值后面添加L,表示这个为长整型,3.0版本已经统一称为为整

第五章 shell学习之文件的排序、合并和分割

sort命令 sort [选项] [输入文件] 选项: -c 测试文件是否已经排序,如果未被排序则输出第一个未被排序的记录 -k 指定排序的域 -m 合并两个已排序的文件,合并的文件也已经排序,如sort -m a1 a2,a1的记录被有序的插入a2 -n 根据数字的大小进行排序,一般放在域号后,如-k3n -o 将输出重定向到指定文件 -r 将排序结果逆向显示 -t 改变域分割符,如-t: -u 去除结果中的重复行 sort和awk联合 例: [[email protected] tmp]#

十五、Android学习笔记_授权过程

1.需要申请App Key和App Secret.不同的开发平台有不同的接入方式,可以参考文档,然后将这两个值放进去. 2.通过OAuth类实现认证,它会自动跳转到认证界面,进行授权,成功之后需要处理回调接口. 3.在第二步调用回调接口时,它会返回用户的基本信息,比如用户id.此时需要将用户id信息保存起来,为后面登录做准备.回调接口的写法就为myapp://AuthorizeActivity,其中scheme全部为小写字母. <activity android:name="com.wei

第五周JAVA学习笔记(五)

将指定目录下的所有文件显示到列表框(JList)组件中, :效果图如下:                  import java.awt.BorderLayout; import java.io.File; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JScrollPane; public class kuang { public st

UNIX 网络编程第五章读后有感

刚看完 UNIX 第五章内容,我想按照自己的方式将自己获得的知识梳理一遍,以便日后查看!先贴上一段简单的 TCP 服务器端代码: 1 #include <sys/socket.h> 2 #include <netinet/in.h> 3 #include <stdio.h> 4 #include <error.h> 5 #include <unistd.h> 6 #include <string.h> 7 #include <s

ASP.NET MVC5 高级编程 第3章 视图

参考资料<ASP.NET MVC5 高级编程>第5版 第3章 视图 3.1 视图的作用 视图的职责是向用户提供界面. 不像基于文件的框架,ASP.NET Web Forms 和PHP ,视图本身并不被访问,浏览器,并不能直接指向一个视图并渲染它.相反视图被控制器渲染,因为控制器提供了渲染所需要的数据. 一般情况下,控制器需要向视图提供一些信息,所以这会传递一个数据转移对象,叫做模型.完成这一过程需要两部分操作,其中一个是检查由控制器提交的模型对象,另一个是将其内容转化为HTML格式. 3.2

ASP.NET MVC5 高级编程 第2章 控制器

参考资料<ASP.NET MVC5 高级编程>第5版 第2章 控制器 控制器:响应用户的HTTP 请求,并将处理的信息返回给浏览器. 2.1 ASP.NET MVC 简介 MVC 模式中的控制器(controller)主要负责响应用户的输入,并且在响应时修改(Model).通过这种方式,MVC 中的Controller 主要关注的是应用程序流,输入数据的处理,以及对相关视图(View)数据来源的提供 MVC 模式中 URL 首先告诉路由去实例化哪个控制器,调用哪个方法,并为该方法提供需要的参数

ASP.NET MVC5 高级编程 第5章 表单和HTML辅助方法

参考资料<ASP.NET MVC5 高级编程>第5版 第5章 表单和HTML辅助方法 5.1 表单的使用 5.1.1 action 和 method 特性 默认情况下,表单发送的是 HTTP Post 请求 EF 对于外键关系,数据库名称等也有约定.这些约定取代了以前需要提供给一个关系对象映射框架的所有映射和配置. GET 方法:GET 请求的所有参数都在URL中,因此可以为GET 请求建立书签. POST 方法:浏览器把输入值放入 HTTP 请求的主体中. 5.2 辅助方法 可以通过视图的H