C# 什么是泛型 ?以及对泛型各方面的一些知识点的整理

 

 

1.1 理解什么是泛型

 

在.NET 2.0,可以成为革命性壮举的, 就是引入了激动人心的特性——泛型。.NET泛型是CLR和高级语言共同支持的一种全新的结构,实现了一种将类型抽象化的通用处理方式。在泛型机制中,我们不再为特定的类型而编码,取而代之的是一种通用的编码方式,因此泛型本质上就是一种代码重用。这种代码重用并非面向对象中通过集成、集合、多态等方式实现;而是实现为一般化、可重用的算法抽象,但在执行效率上与执行特定类型相同。

 

1.2 理解泛型的优点

 

泛型增强了代码的可读性,将大量的安全检查从执行期转移到编译期,从而提高代码的安全性和性能。从根本上来说,泛型实现了类型和方法的参数化。

 

1.3 理解泛型类

 

  • C#自定义泛型类用得最广泛,就是集合(Collection)中。实际上,泛型的产生其中一个原因就是为了解决原来集合类中元素的装箱和拆箱问题。由于泛型的使用,使得集合内所有元素都属于同一类,这就把类型不同的隐患消灭在编译阶段——如果类型不对,则编译错误。
  • 泛型的初始化:泛型是需要进行初始化的。使用T doc = default(T)以后,系统会自动为泛型进行初始化。
  • 定义一个泛型类和定义非泛型类没有没有太大的区别,而主要的不同在于:类型参数化。类型定义时,将指定类型参数(Type Parameter,通常以T 表示),紧随类名,并包含在<>符号内。对于这种具有类型参数的类型,我们称其为:开放式类型;而对于为类型参数传入实际参数的类型,被称为:封闭式类型

 

1.4 理解泛型方法

 

  • 在C#2.0中,方法可以定义特定于其执行范围的泛型参数。
  • 泛型方法,提供了更加多变的灵活性。泛型方法可以存在于泛型类,也可以在于非泛型类中。你可以将类型参数作为某个方法的参数、返回值、或者局部变量,该类型参数可能并不被整个类所需要,而更明确的用于某个方法。

 

例如:

 

 

 

 

 

1.5 理解泛型接口

 

CLR 同样提供了对泛型接口的支持,在.NET 集合类中就实现了多个泛型接口,例如IList<T> 泛型接口的实现可以表示为:

 

 

 

在.NET 框架类库中的泛型接口,还包括 ICollection<T>、Icomparable<T>、Icomparer<T>、IDictionary<Tkey,TValue>等等。

 

 

 

1.6 理解泛型约束

 

约束,指在定义泛型类时,对于能够用于实例化参的类型所做的限制,这种限制能够保证类型参数局限在一定的目标范围,以实现在泛型类中的方法或者运算符能够得到类型参数的支持而不会引起其他问题。这种限制正是通过一个或者多个约束来获得,而约束则是通过where子句来实现的,多个约束之间以逗号隔开。

 

约束主要包括:构造器约束、值类型约束、引用类型约束、基类约束、接口约束等。

 

  • T : new() ,表示类型参数必须具有公共无参构造函数,有多个约束存在时,必须将new()约束置于最后。
  • T : struct ,表示类型参数必须是值类型。
  • T : class  ,表示类型参数必须是引用类型,显示class和struct不能同时指定。
  • T : 基类名,表示类型参数必须是基类及其派生类型,例如 T : Student。但是不能既指定约束基类,又指定class。
  • T : 接口名,表示类型参数必须是指定的接口或者实现了该接口的接口,可以定义多个接口约束,同时约束接口也可以是泛型的。
  • 泛型约束,提供了更强的类型检查,并且更加有效的参数类型有时能够减少不必要的类型转换而改进性能。但是另外一方面,强制性的约束可能使得算法重用的功能受到限制。

 

 

 

1.7 理解泛型使用场景及作用

 

  • 在泛型机制中,我们不再为特定的类型而编码,取而代之的是一种通用的编码方式,因此泛型本质上就是一种代码重用。这种代码重用并非面向对象中通过集成、集合、多态等方式实现;而是实现为一般化、可重用的算法抽象,但在执行效率上与执行特定类型相同。
  • 泛型增强了代码的可读性,将大量的安全检查从执行期转移到编译期,从而提高代码的安全性和性能。从根本上来说,泛型实现了类型和方法的参数化。

 

1.8 理解泛型的可变性-协变性和逆变性

 

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:

 

  • 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的
  • 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的。

 

 

 

在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。

 

 

2.1 泛型概述

1.1.1 泛型广泛用于容器(collections)

1.1.2 命名空间System.Collections.Generic

2.2 泛型的优点.

以前类型的泛化(generalization)是靠类型与全局基类System.Object的相互转换来实现。.

NET框架基础类库的ArrayList容器类,就是这种局限的一个例子。ArrayList是一个很方便的容器类,使用中无需更改就可以存储任何引用类型或值类型

2.3 泛型类型参数.

在泛型类型或泛型方法的定义中,类型参数是一个占位符(placeholder),通常为一个大写字母,

如T。在客户代码声明、实例化该类型的变量时,把T替换为客户代码所指定的数据类型。泛型类,如泛型概述中给出的MyList<T>类,

不能用作as-is,原因在于它不是一个真正的类型,而更像是一个类型的蓝图。要使用MyList<T>,

客户代码必须在尖括号内指定一个类型参数,来声明并实例化一个已构造类型(constructed type)。这个特定类的类型参数可以是编译器识别的任何类型

2.4 类型参数的约束.

若要检查表中的一个元素,以确定它是否合法或是否可以与其他元素相比较,

那么编译器必须保证:客户代码中可能出现的所有类型参数,都要支持所需调用的操作或方法。这种保证是通过在泛型类的定义中,应用一个或多个约束而得到的。

一个约束类型是一种基类约束,它通知编译器,只有这个类型的对象或从这个类型派生的对象,可被用作类型参数。一旦编译器得到这样的保证,它就允许在泛型类中调用这个类型的方法

2.5 泛型类.

泛型类封装了不针对任何特定数据类型的操作。

泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。这些类中的操作,如对容器添加、删除元素,不论所存储的数据是何种类型,都执行几乎同样的操作

2.6 泛型接口.

不论是为泛型容器类,还是表示容器中元素的泛型类,定义接口是很有用的。

把泛型接口与泛型类结合使用是更好的用法,比如用IComparable<T>而非IComparable,以避免值类型上的装箱和拆箱操作。.

NET框架2.0类库定义了几个新的泛型接口,以配合System.Collections.Generic中新容器类的使用

2.7 泛型方法.

1.7.1泛型方法参数声明 

1.7.2泛型方法重载

1.7.3泛型方法约束

2.8 泛型委托.

论是在类定义内还是类定义外,委托可以定义自己的类型参数。

引用泛型委托的代码可以指定类型参数来创建一个封闭构造类型,这和实例化泛型类或调用泛型方法一样

2.9 泛型代码中的default 关键字.

在泛型类和泛型方法中会出现的一个问题是,如何把缺省值赋给参数化类型,此时无法预先知道以下两点:

  1. T将是值类型还是引用类型
  2. 如果T是值类型,那么T将是数值还是结构

 

对于一个参数化类型T的变量t,仅当T是引用类型时,t = null语句才是合法的; t = 0只对数值的有效,而对结构则不行。

这个问题的解决办法是用default关键字,它对引用类型返回空,对值类型的数值型返回零。而对于结构,它将返回结构每个成员,并根据成员是值类型还是引用类型,返回零或空

3.10 C++ 模板和C# 泛型的区别. (知识拓展)

C# Generics and C++ templates are both language features that provide support for parameterized types. However, there are many differences between the two. At the syntax level, C# generics are a simpler approach to parameterized types without the complexity of C++ templates. In addition, C# does not attempt to provide all of the functionality that C++ templates provide. At the implementation level, the primary difference is that C# generic type substitutions are performed at runtime and generic type information is thereby preserved for instantiated objects.

3.11 运行时中的泛型.

当泛型类或泛型方法被编译为微软中间语言(MSIL)后,它所包含的元数据定义了它的类型参数。根据所给的类型参数是值类型还是引用类型,对泛型类型所用的MSIL也是不同的。

    当第一次以值类型作为参数来构造一个泛型类型,运行时用所提供的参数或在MSIL中适当位置被替换的参数,来创建一个专用的泛型类型。[JX3]

1.1 基础类库中的泛型.

泛型类或接口

描述

对应的非泛型类型

Collection<T>

ICollection<T>

为泛型容器提供基类

CollectionBase

ICollection

Comparer<T>

IComparer<T>

IComparable<T>

比较两个相同泛型类型的对象是否相等、可排序。

Comparer

IComparer

IComparable

Dictionary<K, V>

IDictionary<K,V>

表示用键组织的键/值对集合。

Hashtable

IDictionary

Dictionary<K, V>.KeyCollection

表示Dictionary<K, V>中键的集合。

None.

Dictionary<K, V>.ValueCollection

表示Dictionary<K, V>中值的集合。

None.

IEnumerable<T>

IEnumerator<T>

表示可以使用foreach 迭代的集合。

IEnumerable

IEnumerator

KeyedCollection<T, U>

表示有键值的集合。

KeyedCollection

LinkedList<T>

表示双向链表。

None.

LinkedListNode<T>

表示LinkedList<T>中的节点。

None.

List<T>

IList<T>

使用大小可按需动态增加的数组实现 IList 接口

ArrayList

IList

Queue<T>

表示对象的先进先出集合。

Queue

ReadOnlyCollection<T>

为泛型只读容器提供基类。

ReadOnlyCollectionBase

SortedDictionary<K, V>

 表示键/值对的集合,这些键和值按键排序并可按照键访问,实现IComparer<T>接口。

SortedList

Stack<T>

表示对象的简单的后进先出集合。

Stack

posted @ 2020-06-19 15:26  delusion0305  阅读(586)  评论(2编辑  收藏  举报