一文带你搞懂C#泛型

简介: 泛型是.net 2.0中提供的新特性,是框架的一种升级,用于处理用一个事物来代替多种不同需求的情况。下面我们就一块来看一下具体的讲解吧。

引入

如下代码,如果我们想用一个方法来代替该怎么办呢?

/// <summary>
/// 打印个int值
/// </summary>
/// <param name="iParameter"></param>
public static void ShowInt(int iParameter)
{
     Console.WriteLine("This is {0},type={1},parameter={2}",
        typeof(CommonMethod).Name, iParameter.GetType().Name, iParameter);
}
/// <summary>
/// 打印个string值
/// </summary>
/// <param name="sParameter"></param>
public static void ShowString(string sParameter)
{
    Console.WriteLine("This is {0},type={1},parameter={2}",
        typeof(CommonMethod).Name, sParameter.GetType().Name, sParameter);
}
/// <summary>
/// 打印个DateTime值
/// </summary>
/// <param name="oParameter"></param>
public static void ShowDateTime(DateTime dtParameter)
{
    Console.WriteLine("This is {0},type={1},parameter={2}",
        typeof(CommonMethod).Name, dtParameter.GetType().Name, dtParameter);
}


很自然,我们会想到用object作为传入参数的类型,因为object是一切类的基类,这样,我们就可以通过一个方法来实现传入多种类型,如


public static void ShowObject(object oParameter)
{
    Console.WriteLine("This is {0},type={1},parameter={2}",
        typeof(CommonMethod), oParameter.GetType().Name, oParameter);
}


但是,使用object来实现时,可能会存在以下问题:

1.装箱拆箱会有性能损耗

比如传入一个int值时,在装箱拆箱时,需要在堆和栈之间进行操作。

2.类型安全问题,传递的对象没有限制,可能会有安全问题。如需要传入一个people,但是传入一个animal,类型无错误,但编译和运行可能有误。


泛型是什么?

为了解决以上问题,在.net 2.0时,提出了泛型概念,那么什么是泛型呢?

其实,泛型就是为了解决类似于上述问题的,可以说,泛型是用一个事物来代替多种不同类型需求的,泛型的实现采用的是延迟声明的方式,是不同于使用object的继承方式的。那么,泛型的实现具体需要哪些支持呢?

泛型需要的框架支持:

1.编译器能支持类型参数,用占位符(`1、`2...)来表示有几个参数,在运行时确定类型。

2.CLR升级才能支持占位符。运行时,类型确定了,就可以把占位符给替换掉

注意,泛型不是语法糖(什么是语法糖,见文末),而是框架的升级。


泛型的声明和使用

介绍了什么是泛型,我们接着来看,如何定义一个泛型,其实,像List<>,Dictionary<>这样的带有<>的都是泛型声明,如下,是一个自定义的泛型方法


public static void Show<T>(T tParameter)
{
    Console.WriteLine("This is {0},type={1},parameter={2}",
       typeof(CommonMethod), tParameter.GetType().Name, tParameter);
}


如上,在定义时不指定具体类型而使用类似T这样的符号来代替(这里可以是任意的定义,除了关键字)的方式即可声明一个泛型。


泛型声明后,具体该如何使用呢?

因为泛型类型是在运行时解析并确定类型,因此,在调用泛型相关的对象时,需要指定类型。如


CommonMethod.Show<int>(iValue);//调用泛型,需要指定类型参数
CommonMethod.Show(iValue);//如果可以从参数类型推断,可以省略类型参数(这是一种语法糖)
CommonMethod.Show<string>(sValue);


综上所述,泛型在定义时不需要指定类型,但是在使用时,除了可从参数推断类型的情况,必须指定类型。可省略类型的情况,其实是一种语法糖,是编译器提供的功能。


泛型方法的性能

泛型方法的性能跟普通方法一致,是最好的,而且能一个方法满足多个不同类型

这里可以通过运行n(n要很大)多次相对应的方法来测试,方法内可以什么也不处理,只指明是普通方法、泛型方法以及object处理即可。


泛型的应用场景

1.泛型方法:为了一个方法满足不同类型的需求

比如:一个方法完成多实体查询、一个方法完成不同类型的数据展示、任意实体,转换成json字符串。

2.泛型类:一个类型满足不同类型需求,如dictionary、list

3.泛型接口:一个接口满足不同类型需求

4.泛型委托

5.泛型缓存


/// <summary>
/// 每个不同的T,都会生成一份不同的副本
/// 适合不同类型,需要缓存一份数据的场景,效率高
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCache<T>
{
    static GenericCache()
    {
        Console.WriteLine("This is GenericCache 静态构造函数");
        _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
    }
    private static string _TypeTime = "";
    public static string GetCache()
    {
        return _TypeTime;
    }
}


这里只需了解什么是泛型缓存即可,之后我们还会讲到,泛型缓存的性能远高于字典缓存(因为泛型缓存在寄存器处理,字典缓存用hash表处理),缺点是只能对某一类型存储一个对象。

从以上场景我们可以看出来,还是那句话,泛型是用一个事物来代替多种类型的需求的。凡是此类情况,即可以考虑用泛型来处理。


明白了什么是泛型,如何定义泛型以及泛型的应用场景, 那我们再来了解一下泛型约束。那么什么是泛型约束?为什么要有泛型约束呢?


泛型约束

俗话说,没有规矩不成方圆,那么规矩就是约束,试想一下,如果没有交通规则,那我们的道路情况是会井井有条还是混乱不堪?其实也就是有约束才有权利、才能更好的工作。那么泛型约束也是一样的道理,是为了让泛型更高效,还记得在开篇的时候,我提到的object处理形式的第二个缺点吗?对,就是类型安全问题,用约束可以完美的解决类型安全问题。talk is cheap,show you the code。看过之后你应该就会明白。


请注意观察以下2段代码的区别:


public static void ShowObject(object oParameter)
{
    Console.WriteLine("This is {0},parameter={1},type={2}",
        typeof(GenericConstraint), oParameter, oParameter.GetType().Name);
    People people = (People)oParameter;
    Console.WriteLine($"{people.Id}  {people.Name}");
}
public static void Show<T>(T tParameter)
    where T : People, ISports, IWork, new()
{
    Console.WriteLine("This is {0},parameter={1},type={2}",
       typeof(GenericConstraint), tParameter, tParameter.GetType().Name);
    Console.WriteLine($"{tParameter.Id}  {tParameter.Name}");
}


第二段代码定义了一个有泛型约束的方法,其中where T:People...即是泛型的约束定义

其实以上的主要区别就在类型转换的那一行,在处理前需要先显式转换,而泛型类型则可直接调用,区别在于,泛型约束类型其实已经约束了类型,让编译器知道当前的类型是否匹配。那么此时,如果我们在调用泛型方法时传入一个Animal对象,那肯定会报错的。因为Animal不是People。编译不能通过。因此,可以说没有约束其实很局限,因为要实现类型转换,加了约束之后,可以不用转换可以直接调用


既然,泛型约束这么有用,那么泛型约束有哪些种类呢?一般的,我们通常有5大种类的约束。


约束类型

1.基类约束  where T:People(不能是密封类,如where T:String,密封类没有意义,因为没有子类)

2.接口约束  where T:ISport

3.应用类型约束  where T:class

4.值类型约束  where T:struct

5.无参构造函数约束  where T:new()

5种约束可以组合使用,但需要考虑实际情况,比如一般不可能既是class又是struct。默认值用default(T)获取


public T GetT<T, S>()
    where S : People//基类约束
    where S :ISports//接口约束
    where T : class//引用类型约束
    where T : struct//值类型
    where T : new()//无参数构造函数
{
    //return default(T);//default是个关键字,会根据T的类型去获得一个默认值
    return new T();
}


关于泛型可能涉及到一些问题。

问题扩展

1.什么是语法糖?泛型是语法糖吗

语法糖是编译器提供的便捷功能,比如var i=3;以及泛型调用时省略参数类型。语法糖是在编译时确定类型。

泛型不是简单的语法糖,是框架的升级。


2.webservice能用泛型吗?

webservice、WCF都是不能用泛型的,因为服务在发布时类型必须是确定的,而泛型是在运行时确定类型的。

其次,对于这种跨语言的形式,可能别的语言不支持泛型也会有问题。


3.getType()和typeof有区别?

getType是object的一个方法,在对象上调用。

typeof是一个关键字,参数是一个类型

相关文章
|
4月前
|
存储 安全 编译器
C# 11.0中的泛型属性:类型安全的新篇章
【1月更文挑战第23天】C# 11.0引入了泛型属性的概念,这一新特性为开发者提供了更高级别的类型安全性和灵活性。本文将详细探讨C# 11.0中泛型属性的工作原理、使用场景以及它们对现有编程模式的改进。通过深入了解泛型属性,开发者将能够编写更加健壮、可维护的代码,并充分利用C#语言的最新发展。
|
8月前
|
存储 算法 安全
C#三十二 泛型的理解和使用
C#三十二 泛型的理解和使用
21 0
|
2月前
|
存储 安全 Java
34.C#:listT泛型集合
34.C#:listT泛型集合
18 1
|
2月前
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
159 2
|
4月前
|
存储 安全 算法
C# 泛型:类型参数化的强大工具
【1月更文挑战第7天】本文将深入探讨C#语言中的泛型编程,包括泛型的定义、用途、优势以及实际应用。通过类型参数化,泛型允许开发者编写更加灵活且可重用的代码,同时提高程序的类型安全性和性能。本文将通过示例代码和详细解释,帮助读者更好地理解泛型在C#中的重要性和实用性。
|
5月前
|
存储 Java 编译器
【从Java转C#】第五章:泛型
【从Java转C#】第五章:泛型
|
9月前
|
安全 C#
c# 泛型约束
c# 泛型约束
|
10月前
|
存储 C# 索引
C#泛型集合常用方法
C#泛型集合常用方法
43 0
|
10月前
|
安全 C# 索引
C# 泛型集合和非泛型集合(List ArrayLIst)
C# 泛型集合和非泛型集合(List ArrayLIst)
60 0
|
10月前
|
存储 安全 C#
C#里面的不同集合(数组、ArrayList集合、List泛型)
在内存中连续存储,因此可以快速而容易地从头到尾遍历元素,可以快速地修改元素