什么是泛型

前言

一些同学对泛型不是很了解,只知道使用,不知道为什么使用,有什么好处等;
下面我来谈谈自己对泛型的理解。

泛型概念

泛型就是把类型参数化也叫参数化类型。通俗来讲泛型就是“通用类型”,它可以代替任何的数据类型,使类型参数化,从而达到只实现一个方法就可以操作多种数据类型的目的。
原理:延时声明,在运行时进行编译。

微软官方定义:泛型是为所存储或使用的一个或多个类型具有占位符的类、结构、接口和方法。 泛型集合类可以将类型形参用作其存储的对象类型的占位符;类型形参呈现为其字段的类型和其方法的参数类型。

泛型将类型参数的概念引入 .NET,这样就可在设计类和方法时,不必确定一个或多个具体参数,它的具体参数可延迟到客户端代码中声明、实现。

通过使用泛型类型参数 T,这意味着使用泛型的类型参数T,可以多种形式调用,运行时类型转换避免了装箱操作的代价和风险。
如下所示:

// 声明一个泛型类,T是类型占位符.
public class GenericList<T>
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // 声明一个int类型的集合.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // 声明一个string类型的集合.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // 声明一个对象的类型集合.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}

使用泛型的优点:

  • 类型安全。泛型将类型检查转移到编译器。 没有必要编写代码来测试正确的数据类型,因为它会在编译时强制执行。 降低了强制类型转换的必要性和运行时错误的可能性。

  • 代码更少,代码重用。例如:泛型List< T >。

  • 性能更好。无须装箱和取消装箱的操作,节约时间。

泛型类型的概述

在 .NET 中,开发人员随时会使用泛型,有时隐式使用,有时显式使用。 在 .NET 中使用 LINQ 时,你是否曾经注意到,使用的正是 IEnumerable< T >? 或者,你是否曾经看到过有关使用实体框架来与数据库通信的“泛型存储库”在线示例,其中的大多数方法返回 IQueryable< T >? 你可能很想知道,这些示例中的 T 是什么意思,为什么要使用它?

泛型在 .NET Framework 2.0 中首次引入,它本质上是一个“代码模板”,可让开发者定义类型安全数据结构,无需处理实际数据类型。 例如,List< T > 是一个可以声明的泛型集合,可与 List< int >、List< string > 或 List< Person > 等任何类型结合使用。

为方便理解泛型的作用,让我们看看添加泛型之前和之后的一个特定类:ArrayList。 在 .NET Framework 1.0 中,ArrayList 元素属于 Object 类型。 添加到集合的任何元素都会以静默方式转换为 Object。 从列表读取元素时,会发生相同的情况。 此过程称为装箱和取消装箱,它会影响性能。 但除了性能之外,在编译时无法确定列表中的数据的类型,这会形成一些脆弱的代码。 泛型解决了此问题,它可以定义每个列表实例将要包含的数据类型。 例如,只能将整数添加到 List,只能将人员添加到 List。

泛型还可以在运行时使用。 运行时知道你要使用的数据结构类型,并可以更高效地将数据结构存储在内存中。

示例:

  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;

  namespace GenericsExample {
    class Program {
      static void Main(string[] args) {
        //泛型集合
        List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
        //非泛型集合
        ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
        // 为泛型集合排序开启定时器
        Stopwatch s = Stopwatch.StartNew();
        ListGeneric.Sort();
        s.Stop();
        Console.WriteLine($"泛型排序: {ListGeneric}  \n 花费时间: {s.Elapsed.TotalMilliseconds}ms");

        //为非泛型集合排序开启定时器
        Stopwatch s2 = Stopwatch.StartNew();
        ListNonGeneric.Sort();
        s2.Stop();
        Console.WriteLine($"非泛型排序: {ListNonGeneric}  \n 花费时间: {s2.Elapsed.TotalMilliseconds}ms");
        Console.ReadLine();
      }
    }
  }

输出结果:

泛型排序: System.Collections.Generic.List`1[System.Int32]
 花费时间: 0.0034ms
非泛型排序: System.Collections.ArrayList
 花费时间: 0.2592ms

在此处可以看到的第一个优点是,泛型列表的排序比非泛型列表要快得多。 还可以看到,泛型列表的类型是不同的 ([System.Int32]),而非泛型列表的类型已通用化。 由于运行时知道泛型 List 的类型是 Int32,因此可以将列表元素存储在内存中的基础整数数组内;而非泛型 ArrayList 必须将每个列表元素强制转换为对象。 如本示例中所示,多余的强制转换会占用时间,降低列表排序的速度。

五种基础泛型约束

约束告知编译器类型参数必须具备的功能。 在没有任何约束的情况下,类型参数可以是任何类型。 编译器只能假定 System.Object 的成员,它是任何 .NET 类型的最终基类。如果客户端代码使用不满足约束的类型,编译器将发出错误。 通过使用 where 上下文关键字指定约束。 下表列出了五种类型的约束:

编号约束描述
1where T : struct类型参数必须是不可为null的值类型。由于所有值类型都具有可访问的无参构造函数,因此struct约束表示new() 约束,并且不能与new() 约束结合使用。
2where T : class类型参数必须是引用类型。此约束还应用于任何类、接口、委托或数组类型。
3where T : new()类型参数必须具有公共无参构造函数。
4where T : <基类名>类型参数必须是指定的基类或派生自指定的基类。
5where T : <接口名称>类型参数必须是指定的接口或实现指定的接口。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值