C#高级静态语言效率利器之泛型详解

 更新时间:2023年02月27日 11:19:19   作者:微小冷  
所谓泛型,就是创建一个函数,对所有数据类型都生效。这篇文章就来带大家深入了解一下C#中高级静态语言效率利器——泛型的使用,需要的可以参考一下

引入

所谓泛型,就是创建一个函数,对所有数据类型都生效。最常见的例子就是运算符,毕竟1+1=2,1.0+1.0=2.0,足以看出+是对多种数据类型起作用的。

但是,如想创建一个函数add(int a, int b),那么输入add(1.0, 1.0)是肯定要报错的,VS直接就给标红了。

泛型的出现,就很好地解决了这个尴尬的问题

T add<T>(T a, T b) 
{
    dynamic d1 = a;
    dynamic d2 = b;
    return (T)(d1 + d2);
}

Console.WriteLine(add<int>(1, 1));
Console.WriteLine(add<double>(1.0, 1.0));

上面代码中,T表示某种数据类型,在调用函数add时,根据add后面的<>加以声明。

但如果就此就写return a+b显然也是不行的,因为+这种运算符并没有对T进行重载,编辑器并不会允许两种未知的类型相加。

这个时候就需要用到dynamic,用来让编辑器放弃类型检查,将任何可能发生的错误都留给运行阶段。

最后,运行结果为

2
2

类型约束

dynamic用着确实爽,但后果就是责任自负,这玩意要是用在团队协作的场合,简直就是灾难,毕竟并非所有对象都可以驾驭加法。

所以,C#的泛型,是可以被约束的泛型,关键就是where,将上述代码写为

T add<T>(T a, T b) where T : struct{
    dynamic d1 = a;
    dynamic d2 = b;
    return (T)(d1 + d2);
}

where T : struct表示T必须是数值类型的一种,所以编译器的类型检查仍会发挥作用,在调用add时,如果T不是数值类型,就会报错。

C#一共有5种约束方案,列表如下

类别条件
structT必须是值类型
classT必须是引用类型
new()T必须有无参数的构造函数
基类名T必须是基类或派生自基类
接口名T必须是指定接口
裸类型

 不同类型的约束,或相同类型不同种类的约束,一般是可以混用的,如果不能混用,编译器会提醒。比如struct几乎不能和其他类型混用。如果new()参与了约束,则放在最后。

子类泛型

除了函数可以采用泛型,类当然也可以,不仅可以,而且还能继承。

class MyList<T>
{
    public T[] a;
    public MyList(){}       //无参数的构造函数,用于继承
    public MyList(int n){
        a = new T[n];
    }
    public T this[int index]{
        get => a[index];
        set => a[index] = value;
    }

​​​​​​​}

MyList相当于是给数组套了一层壳,其构造函数并不存在什么难以理解的地方,唯一有些问题的可能是下面的索引器public T this[int index],这种写法可以实现方括号形式的索引。

可以测试一下

var a = new MyList<int>(5);
for (int i = 0; i < 5; i++)
{
    a[i] = i;
    Console.WriteLine(a[i]);
}

结果就不粘贴了,接下来新建一个子类

class MyStack<T> : MyList<T>
{
    public MyStack(int n)
    {
        a = new T[n];
    }
    public T Pop()
    {
        T p = a[a.Length- 1];
        a = a[0..(a.Length-1)];
        return p;
    }
}

然后测试一下

var a = new MyStack<int>(3);
for (int i = 0; i < 3; i++)
{
    a[i] = i;
}

for (int i = 0; i < 3; i++)
{
    Console.WriteLine(a.Pop());
}

结果为

2
1
0

常用的泛型数据结构

C#通过泛型定义了很多数据结构,例如在讲解switch...case时提到的字典

Dictionary<int, string> card = new Dictionary<int, string>
{
    {1,"A" },
    {11, "J" },
    {12, "Q" },
    {13, "K" }
};

这种<U, V>的写法,正是泛型的特点,其中U, V就是可以随意声明的变量。如果查看字典的类型参数,可以发现其定义方法是这样的

public class Dictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, ... where TKey : notnull

考虑到本节并不是为了将面向对象,所以字典继承的那一大坨类就省略了,关键是where Tkey:notnull,也就是说,字典对键值对的要求只有一个,就是键不得为null。

除了字典之外,还有一些常见的数据结构采用了泛型,列表如下,没事儿可以练习练习。

数据结构说明常用方法
List<T>泛型列表Add, Remove, RemoveAt
LinkedList<T>双端链表AddFirst, AddLast, RemoveFirst, RemoveLast
Queue<T>先进先出列表Enqueue, Dequeue
Stack<T>栈,先进后出Push, Pop

泛型委托

委托,是函数的函数;泛型,可以让函数的参数类型更加灵活,二者结合在一起,就是更加灵活的函数的函数,即泛型委托。

只要学过了泛型和委托,那么对泛型委托将毫无理解上的难度,回想前面定义的运算符委托

delegate int Op(int a, int b);

再回想定义泛型时的<T>,那么泛型委托可以非常简单地定义出来

delegate T Op<T>(T a, T b);

然后就可以根据委托,建立一个泛型函数

T add<T>(T a, T b)
{
    dynamic d1 = a;
    dynamic d2 = b;
    return (T)(d1 + d2);
}
var addTest = new Op<int>(add<int>);
//也可以省略add后的<int>,写成下面的形式
//var addTest = new Op<int>(add);
Console.WriteLine(addTest(3, 5));

运行之后控制台出现了8,就是这么简单。

到此这篇关于C#高级静态语言效率利器之泛型详解的文章就介绍到这了,更多相关C#泛型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Unity实现OCR文字识别功能

    Unity实现OCR文字识别功能

    这篇文章主要介绍了通过Unity接入百度AI接口,实现OCR文字识别功能,文中的实现步骤讲解详细,对我们学习或工作有一定的参考价值,需要的可以了解一下
    2022-01-01
  • C#操作windows系统进程的方法

    C#操作windows系统进程的方法

    这篇文章介绍了C#操作windows系统进程的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • DevExpress之ChartControl用法实例总结

    DevExpress之ChartControl用法实例总结

    这篇文章主要介绍了DevExpress之ChartControl用法实例总结,需要的朋友可以参考下
    2014-08-08
  • C#实现线性搜索算法

    C#实现线性搜索算法

    线性搜索算法是一种基本的搜索算法,通过逐个比较元素来查找目标元素,学习线性搜索算法有助于培养算法思维和编程能力,对于初学者来说是一种重要的算法训练,感兴趣的可以了解一下
    2024-10-10
  • C#程序中session的基本设置示例及清除session的方法

    C#程序中session的基本设置示例及清除session的方法

    这篇文章主要介绍了C#程序中session的基本设置示例及清除session的方法,是C#入门学习中的基础知识,需要的朋友可以参考下
    2016-04-04
  • C#实现身份证实名认证接口的示例代码

    C#实现身份证实名认证接口的示例代码

    身份证实名认证,即通过姓名和身份证号校验个人信息的匹配程度,广泛应用于金融、互联网等多个领域,本文主要介绍了C#实现身份证实名认证接口的示例代码,感兴趣的可以了解一下
    2024-09-09
  • C#主线程堵塞问题的解决方案

    C#主线程堵塞问题的解决方案

    这篇文章主要介绍了C#主线程堵塞问题的解决方案,在C#中,异步方法和async/await关键字是用来解决主线程阻塞的有效方式,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2024-03-03
  • C#实现Modbus通信功能的示例详解

    C#实现Modbus通信功能的示例详解

    Modbus作为一种开放且广泛采用的通信协议,在实现设备间数据交换方面发挥着至关重要的作用,它不仅支持多种物理层接口(如RS-232, RS-485, 以及以太网),还因其简单易用的特点而被大家所青睐,本文通过实际示例介绍如何在C#项目中轻松实现Modbus通信功能
    2024-11-11
  • Unity实现虚拟摇杆

    Unity实现虚拟摇杆

    这篇文章主要为大家详细介绍了Unity实现虚拟摇杆,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 在C#中获取端口号与系统信息的高效实践

    在C#中获取端口号与系统信息的高效实践

    在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C# 作为一种广泛应用的编程语言,提供了丰富的 API 来帮助开发者获取计算机的硬件信息和网络状态,本篇博客将带你深入探索如何在 C# 中高效获取端口号和系统信息
    2025-01-01

最新评论