理解C#泛型

在C# 2.0中引入了泛型,泛型的出现解决了编码中的很多问题。相信大家一定经常用到"System.Collections.Generic"命名空间中的泛型集合类("Generic"就是泛型的意思)。在C# 1.0中,我们还在使用"System.Collections"命名空间中的非泛型集合类,那么看看我们在没有泛型的时候遇到的问题。

问题1:强制类型转换

ArrayList stuList = new ArrayList();
Student wilber = new Student { Name = "Wilber", Age = 27, Gender = "Male" };
stuList.Add(wilber);
Student stu = (Student)stuList[0];
stuList.Add(10);

在使用非泛型集合ArrayList时,所有的对象都是以object类型加入ArrayList,当对象从ArrayList取出的时候也是object类型,这时我们就需要进行强制类型转换,如果转换不当,就会得到一个运行时的错误;即使我们向ArrayList添加不同类型的对象时,也不会报错(例如上面向stuList中加入了一个int值)。

问题2:装箱和拆箱

在上面的例子中,如果我们使用ArrayList存放一组值类型的数据(例如一组int值),存入时,每个值类型的数据都要进行装箱为object类型;取出时,每个object类型的数据又要进行拆箱操作。

可以看到,在使用非泛型集合的时候,用户需要自己进行类型转换,并且可能遇到运行时的类型转换异常;同时,对于值类型的操作 ,非泛型集合会有装箱和拆箱带来的效率问题。

泛型的出现

对于上面的问题,我们可以使用C# 2.0中的泛型集合。

这样一来,我们就通过类型参数(例子中的Student)来限制List可以包含的实例类型,从而避免的强制类型转换。

同时,通过类型参数,编译器可以进行类型检查,当试图往List中存入一个与类型参数不匹配的对象的时候,编译器就是给出错误提示。

List<Student> stuList = new List<Student>();
Student wilber = new Student { Name = "Wilber", Age = 27, Gender = "Male" };
stuList.Add(wilber);
Student stu = stuList[0];
stuList.Add(10);

泛型中的术语

下面我们看看泛型中的一些概念和术语。

泛型有两种表现形式:泛型类型(包括类、接口、委托和结构,没有泛型枚举)和泛型方法。在泛型类型和泛型方法中都会有类型参数,当通过泛型类型实例化对象或者对泛型方法调用的时候,都需要使用一个真实的类型来代替类型参数。

类型参数是真实类型的占位符,在泛型声明过程中,所有的类型参数放在一对间括号中(<>),通过逗号分隔。

泛型类型

根据类型参数不同的指定类型实参的情况,泛型类型可以分为:

  • 如果没有为类型参数提供类型实参,那么声明的就是一个未绑定泛型类型(unbound generic)
  • 如果指定了类型实参,该类型就称为已构造类型(constructed type),然而已构造类型又可以是开放类型或封闭类型的
    • 包含类型参数的类型就是开放类型(open type)(所有的未绑定的泛型类型都属于开放类型的),
    • 每个类型参数都指定了类型实参就是封闭类型(closed type)

类型是对象的蓝图,我们可以通过类型来实例化对象;那么对于泛型来说,未绑定泛型类型是以构造泛型类型的蓝图,已构造泛型类型又是实际对象的蓝图。

下图就是一个简单的例子,Dictionary<TKey, TValue>就是一个泛型类型(未绑定泛型类型,开放类型);通过制定类型参数,可以得到不同的封闭类型;通过不同的封闭类型有可以构造不同的实例。

泛型方法

我们都已经习惯了方法的参数和返回值拥有固定的类型,这里就看看“参数化”的方法。对于泛型方法,可以理解为拥有类型参数的方法。

对于上面例子中Dictionary<TKey, TValue>这个泛型类型,有很多方法可以使用,例如:

  • void Add(TKey, key, TValue value)
  • bool ContainsValue(TValue value)
  • bool ContainsKey(TKey key)

注意,这些方法中没有一个是真正的泛型方法,他们只是使用了泛型类型的类型参数

真正的泛型方法应该拥有自己的类型参数,当我们使用泛型方法的时候,要给泛型方法的类新参数指定类型实参,接下来看一个泛型方法的例子。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("The bigger one is {0}", GetBiggerOne<int>(3,9));
        Console.WriteLine("The bigger one is {0}", GetBiggerOne<string>("Hello", "World"));

        Console.Read();
    }

    public static T GetBiggerOne<T>(T itemOne, T itemTwo) where T : IComparable
    {
        if (itemOne.CompareTo(itemTwo) > 0)
        {
            return itemOne;
        }
        return itemTwo;
    }
}

在上面的例子中,我们使用泛型方法来实现一个两个元素比较的例子,我们看到方法"GetBiggerOne"拥有自己的类型参数,当我们看到一个泛型方法时,可以一步步用真实的类型替换泛型方法中的类型参数,这样就会简化我们的分析。

对于泛型的类型约束,将在下面一篇文章介绍。

泛型的优点

根据上面的分析,可以看到泛型有一些的优点:

  • 代码重用

    • 泛型提供的代码的重用,确切的说应该是 "逻辑和算法的重用"。从前面的泛型方法例子可以看到,通过泛型可以避免为每种特定的类型实现一个比较方法。
  • 类型安全
    • 泛型类型保证了类型安全,可以在编译期就发现类型不匹配的问题,而不是等到运行时
  • 效率
    • 避免值类型的装箱和拆箱引起的效率问题(后面会简单介绍为什么泛型可以避免装箱和拆箱)

总结

泛型的出现,给我们带来了很多好处,泛型实现了类型和方法的"参数化"。

基于泛型,我们可以实现代码重用,并且泛型为我们提供了类型安全检查。对于值类型的操作,通过泛型可以避免装箱和拆箱带来的性能损失。

同样C# 2.0 以后,就建议只在代码中使用支持泛型的集合类了(System.Collections.Generic)。

时间: 2024-12-21 15:19:06

理解C#泛型的相关文章

理解C#泛型(转)

理解C#泛型 http://www.cnblogs.com/wilber2013/p/4292240.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber2013/p/4291435.html

深入理解C#泛型

前面两篇文章介绍了C#泛型的基本知识和特性,下面我们看看泛型是怎么工作的,了解一下泛型内部机制. 泛型内部机制 泛型拥有类型参数,通过类型参数可以提供"参数化"的类型,事实上,泛型类型的"类型参数"变成了泛型类型的元数据,"运行时"在需要的时候会利用他们构造恰当的类型,通过这些类型,我们有可以实例化不同类型的对象.也就是说,未绑定泛型类型是以构造泛型类型的蓝图,已构造泛型类型又是实际对象的蓝图. 分析泛型IL代码 下面看一个例子,在这个例子中定义

理解Java泛型 通配符 ? 以及其使用

什么是泛型: 泛型从字面上理解,是指一个类.接口或方法支持多种类型,使之广泛化.一般化和更加通用.Java中使用Object类来定义类型也 能实现泛型,但缺点是造成原类型信息的丢失,在使用中容易造成ClassCastException. Java泛型带到的好处: 使得一个类或方法中的类型参数化,最终达到代码复用的效果.( 不使用泛型,你可能需要每种情况的类或方法都要定义一遍 ) 实现类型检查的功能,避免ClassCastException.(这是相对于使用Object类型实现泛型而言.因为我可以

深入理解java泛型

一. 什么是泛型? 泛 型(Generic type 或者 generics)是对 简单的理解,就是对类型的参数化,比如我们定义一个类属性或者实例属性时,往往要指定具体的类型,如Integer.Person等等, 但是如果使用了泛型,我们把这些具体的类型参数化,用一个广泛的可以表示所有类型的"类型"T来定义,那这个T就是泛型的表示. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给

Java中的泛型 (上) - 基本概念和原理

本节我们主要来介绍泛型的基本概念和原理 后续章节我们会介绍各种容器类,容器类可以说是日常程序开发中天天用到的,没有容器类,难以想象能开发什么真正有用的程序.而容器类是基于泛型的,不理解泛型,我们就难以深刻理解容器类.那,泛型到底是什么呢? 什么是泛型? 一个简单泛型类 我们通过一个简单的例子来说明泛型类的基本概念.实现原理和好处. 基本概念 我们直接来看代码: public class Pair<T> { T first; T second; public Pair(T first, T se

计算机程序的思维逻辑 (37) - 泛型 (下) - 细节和局限性

35节介绍了泛型的基本概念和原理,上节介绍了泛型中的通配符,本节来介绍泛型中的一些细节和局限性. 这些局限性主要与Java的实现机制有关,Java中,泛型是通过类型擦除来实现的,类型参数在编译时会被替换为Object,运行时Java虚拟机不知道泛型这回事,这带来了很多局限性,其中有的部分是比较容易理解的,有的则是非常违反直觉的. 一项技术,往往只有理解了其局限性,我们才算是真正理解了它,才能更好的应用它. 下面,我们将从以下几个方面来介绍这些细节和局限性: 使用泛型类.方法和接口 定义泛型类.方

在MVC程序中,使用泛型仓储模式和工作单元实现增删查改

在这片文章中,我将自己动手为所有的实体:写一个泛型仓储类,还有一个工作单元. 工作单元的职责就是:为每一个实体,创建仓储实例.仓储(仓库)的职责:增删查改的功能实现. 我们将会在控制器中,创建工作单元类(UnitOfWork)的实例,然后根据实体,创建仓储实例,再就是使用仓储里面的方法,做操作了. 下面的图中,解释了,仓储和EF 数据上文的关系,在这个图里面,MVC控制器和仓储之间的交互,是通过工作单元来进行的,而不是直接和EF接触. 那么你可能就要问了,为什么要使用工作单元??? 工作单元,就

Java泛型简明教程

Java泛型简明教程 博客分类: Java综合 JavaApple数据结构CC++ Java泛型简明教程 本文是从 Java Generics Quick Tutorial 这篇文章翻译而来. 泛型是Java SE 5.0中引入的一项特征,自从这项语言特征出现多年来,我相信,几乎所有的Java程序员不仅听说过,而且使用过它.关于Java泛型的教程,免费的,不免费的,有很多.我遇到的最好的教材有: The Java Tutorial Java Generics and Collections ,

Java泛型-- 通配符

转自:http://blog.csdn.net/flfna/article/details/6576394 ———————————————————————————————————————————— 通配符 在本文的前面的部分里已经说过了泛型类型的子类型的不相关性.但有些时候,我们希望能够像使用普通类型那样使用泛型类型: ◆ 向上造型一个泛型对象的引用 ◆ 向下造型一个泛型对象的引用 向上造型一个泛型对象的引用 例如,假设我们有很多箱子,每个箱子里都装有不同的水果,我们需要找到一种方法能够通用的处