• 什么是泛型


    前言

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

    泛型概念

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

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

    泛型将类型参数的概念引入 .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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    使用泛型的优点:

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

    • 代码更少,代码重用。例如:泛型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();
          }
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    输出结果:

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

    在此处可以看到的第一个优点是,泛型列表的排序比非泛型列表要快得多。 还可以看到,泛型列表的类型是不同的 ([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 : <接口名称>类型参数必须是指定的接口或实现指定的接口。
  • 相关阅读:
    WPF十六(页面内嵌加载)
    用CSS实现一个扇形
    springboot大学生就业规划系统毕业设计-附源码191451
    深入理解java泛型
    【Android Jetpack】DataStore的介绍
    Java 20 新功能介绍
    debian和ubuntu的核心系统和系统命令的区别
    电脑一键重装系统发现内存占用率过高怎么办
    Jenkins部署spring boot项目
    部分聚合平台“真自营 假聚合”?专家:扰乱市场公平秩序
  • 原文地址:https://blog.csdn.net/qq_35434967/article/details/127818527