图解C#高级教程(三):泛型

news2024/12/25 2:35:29

本讲用许多代码示例介绍了 C# 语言当中的泛型,主要包括泛型类、接口、结构、委托和方法。

文章目录

  • 1. 为什么需要泛型?
  • 2. 泛型类的定义
    • 2.1 泛型类的定义
    • 2.2 使用泛型类创建变量和实例
  • 3. 使用泛型类实现一个简单的栈
    • 3.1 类型参数的约束
    • 3.2 Where 子句
    • 3.3 约束类型和次序
  • 4. 泛型方法
  • 5. 泛型结构
  • 6. 泛型委托
  • 7. 泛型接口

1. 为什么需要泛型?

在之前的教程中,我们使用的自定义类都是具有具体的类型。如果对类型进行抽象,该类型就是泛型,即一种广泛的类型。例如,我们有一个水果篮,水果篮中可以放不同种类的水果,例如苹果、香蕉、菠萝等等。这个水果篮可以放置的水果就是一种泛型,水果可以代指很多不同种类的水果。

为什么需要泛型?泛型的好处之一就是能够提高代码的复用性。例如,我们有一种栈数据结构,栈当中的类型可以是 intfloat等等,但很多操作都是相同的:入栈、出栈、获取栈大小,所不同的是数据类型的不同。利用泛型,我们只需要实现一套代码,而不需要针对不同的数据类型分别实现各自的一套栈代码。

在 C# 中,泛型又称为参数化类型。泛型可以作用在类、函数、结构、委托和接口,由此衍生出泛型类、泛型函数、泛型结构、泛型委托和泛型接口的概念。
在这里插入图片描述

2. 泛型类的定义

2.1 泛型类的定义

2.2 使用泛型类创建变量和实例

在使用泛型类创建变量时,可以使用关键字 var,让编译器根据 = 右边的类型自动推断变量的类型。

using System;

class SomeClass<T1, T2>
{
    public T1 Property1 { get; set; }
    public T2 Property2 { get; set; }

    // Constructor
    public SomeClass(T1 property1, T2 property2)
    {
        Property1 = property1;
        Property2 = property2;
    }
}
class Program
{
    static void Main(string[] args)
    {
        SomeClass<int, float> sc = new SomeClass<int, float>(10, 3.14f);
        var sc2 = new SomeClass<string, bool>("Hello", true);  // 让编译器推断类型
        Console.WriteLine(sc.Property1);
        Console.WriteLine(sc.Property2);
        Console.WriteLine(sc2.Property1);
        Console.WriteLine(sc2.Property2);
    }
}

输出:

10
3.14
Hello
true

需要注意的是,为具体类分配的内存是存储在堆上的。

3. 使用泛型类实现一个简单的栈

using System;

namespace GenericStackExample
{
    // 定义一个泛型栈类  
    public class Stack<T>
    {
        // 使用数组来存储栈中的元素  
        private T[] _items;
        // 栈顶元素的索引(初始化为-1表示栈为空)  
        private int _top;
        // 栈的容量  
        private int _capacity;
        
        // 构造函数,初始化栈的容量  
        public Stack(int capacity = 10)
        {
            _capacity = capacity;
            _items = new T[capacity];
            _top = -1;
        }
        // 检查栈是否为空  
        public bool IsEmpty()
        {
            return _top == -1;
        }
        // 检查栈是否已满  
        public bool IsFull()
        {
            return _top == _capacity - 1;
        }
        // 入栈操作  
        public void Push(T item)
        {
            if (IsFull())
            {
                throw new InvalidOperationException("Stack is full.");
            }

            _items[++_top] = item;
        }
        // 出栈操作  
        public T Pop()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("Stack is empty.");
            }

            return _items[_top--];
        }
        // 查看栈顶元素(不移除)  
        public T Peek()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("Stack is empty.");
            }
            return _items[_top];
        }
        // 获取栈的大小(元素数量)  
        public int Size()
        {
            return _top + 1;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Stack<int> intStack = new Stack<int>(5); // 创建一个整型栈  
            // 入栈操作  
            intStack.Push(1);
            intStack.Push(2);
            intStack.Push(3);

            // 访问栈顶元素  
            Console.WriteLine($"栈顶元素是: {intStack.Peek()}");
            // 出栈操作  
            Console.WriteLine($"出栈元素: {intStack.Pop()}");
            // 获取栈的大小  
            Console.WriteLine($"栈的大小是: {intStack.Size()}");
            // 尝试在空栈上执行出栈操作以演示异常  
            try
            {
                intStack.Pop();
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

3.1 类型参数的约束

现在我们能够设计出泛型类,但是泛型类型它本身提供什么方法,我们是不知道的。例如下面的泛型类:

class Simple<T>
{
	static public bool LessThan(T i1, T i2)
	{
		return i1 < i2;
	}
}
...

但不是所有类型 T 都实现了小于运算符,所以会报出错误:
在这里插入图片描述
为此,我们需要告诉编译器关于泛型类型的额外信息,让其知道参数可以接受哪些类型。这些额外的信息叫做约束(constrain)。没有任何约束的类型参数称为未绑定的类型参数(unbounded type parameter)。

3.2 Where 子句

约束使用 where 子句列出:
在这里插入图片描述

3.3 约束类型和次序

共有 5 种类型的约束,如下表所示:

约束类型描述
某个具体的类名只有这个类型的类或从它继承的类才能用作类型参数
class任何引用类型,包括类、数组、委托和接口都可以用作类型参数
struct任何值类型都可以用作类型参数
接口名只有这个接口或实现这个接口的类型才能用作类型参数
new()任何带有无参公共构造函数的类型都可以用作类型参数。这叫做构造函数约束

对不同类型的 where 约束可以以任何顺序列出。但是,where 子句内的约束必须遵循一定的顺序:

  • 最多只能具有一个主约束,有的话必须放到第一位;
  • 可以有任意多的接口名约束;
  • 如果存在构造函数约束,则必须放到最后。
    在这里插入图片描述
    下面是一些例子:
class SortedList<S>
    where S: IComparable<S>
{ }

class LinkedList<M, N>
    where M: IComparable<M>
    where N: ICloneable
{ }

class MyDictionary<KeyType, ValueType>
    where KeyType: IComparable<KeyType>,
    new()
{ }

4. 泛型方法

泛型方法的定义如下:
在这里插入图片描述

泛型方法的使用:

void DoStuff<T1, T2> (T1 t1, T2 t2)
{
    T1 someVar = t1;
    T2 otherVar = t2;
}

DoStuff<int, string>(10, "Hello");
DoStuff<int, long>(iVal, lVal);

当参数类型列表的类型和方法列表中的类型相同时,在使用泛型方法时可以省略对参数类型的指定,例如:

public void MyMethod<T> (T val) {}
int myInt = 5;
MyMethod(myInt);

泛型方法的使用示例:

class Simple
{
    static public void ReverseAndPrint<T>(T[] arr)
    {
        Array.Reverse(arr);
        foreach (var item in arr)
        {
            Console.Write("{0}, ", item.ToString());
            Console.WriteLine();
        }
    }
}

class Program
{
    static void Main()
    {
        // 创建整数、字符串、浮点型数组  
        int[] intArray = { 1, 2, 3, 4, 5 };
        string[] stringArray = { "hello", "world" };
        float[] floatArray = { 1.1f, 2.2f, 3.3f };

        // 调用泛型方法,反转并打印数组  
        Simple.ReverseAndPrint(intArray);
        Simple.ReverseAndPrint<int>(intArray);

        Simple.ReverseAndPrint(stringArray);
        Simple.ReverseAndPrint<string>(stringArray);

        Simple.ReverseAndPrint(floatArray);
        Simple.ReverseAndPrint<float>(floatArray);

    }
}

输出结果:
在这里插入图片描述

5. 泛型结构

泛型结构和泛型类的定义类似,直接给出例子。

struct PieceOfData<T>
{
    public T _data { get; set; }
    public PieceOfData(T data)
    {
        _data = data;
    }
}

class Program
{
    static void Main()
    {
        PieceOfData<int> piece1 = new PieceOfData<int>(10);
        PieceOfData<string> piece2 = new PieceOfData<string>("Hello");

        Console.WriteLine(piece1._data);
        Console.WriteLine(piece2._data);
    }
}

6. 泛型委托

在这里插入图片描述
委托类型当中可以使用泛型的地方:

  • 返回类型
  • 类型参数
  • where子句

泛型委托的例子:

delegate void MyDelegate<T>(T value);

class Simple
{
    static public void PrintString(string s)
    {
        Console.WriteLine(s);
    }

    static public void PrintUpperString(string s)
    {
        Console.WriteLine(s.ToUpper());
    }
}

class Prgram
{
    static void Main()
    {
        MyDelegate<string> d1 = new MyDelegate<string>(Simple.PrintString);
        d1 += Simple.PrintUpperString;

        d1("Hello");
    }
}

输出:

hello
HELLO

7. 泛型接口

interface IMyIfc<T>
{
    T ReturnIt(T invalue);
}

class Simple: IMyIfc<int>, IMyIfc<string>
{
    // 因为 Simple 类实现了两个接口,所以它必须实现两个接口的相同方法。
    public int ReturnIt(int invalue)
    {
        return invalue;
    }

    public string ReturnIt(string invalue)
    {
        return invalue;
    }
}

class Program
{
    static void Main()
    {
        IMyIfc<int> intIfc = new Simple();
        IMyIfc<string> stringIfc = new Simple();

        Console.WriteLine(intIfc.ReturnIt(10));
        Console.WriteLine(stringIfc.ReturnIt("Hello"));
    }
}

本章小结:主要通过例子讲解了 C# 语言当中泛型{类、接口、结构、委托、方法}的用法。

各位道友,码字不易,记得一键三连啊。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2181802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不相同的二叉搜索树

给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5示例 2&#xff1a; 输入&#xff1a;n 1 输出&#xff1a;1提…

数字教学时代:构建高效在线帮助中心的重要性

在数字化教学日益普及的今天&#xff0c;教育领域正经历着前所未有的变革。随着在线课程、虚拟教室、智能学习平台等数字化工具的广泛应用&#xff0c;教育资源的获取方式和学习模式发生了深刻变化。然而&#xff0c;这种变革也带来了新的挑战&#xff0c;其中之一便是如何确保…

YashanDB Docker镜像制作

本文作者&#xff1a;YashanDB中级服务工程师鲍健昕 为什么需要Docker部署数据库 常规使用 yasboot 部署数据库的方法&#xff0c;操作流程复杂&#xff0c;需要配置许多配置文件以及环境变量&#xff0c;不同用户使用的环境不同&#xff0c;那么环境配置也会存在差异&#x…

YOLO11震撼发布!

非常高兴地向大家介绍 Ultralytics YOLO系列的新模型&#xff1a; YOLO11&#xff01; YOLO11 在以往 YOLO 模型基础上带来了一系列强大的功能和优化&#xff0c;使其速度更快、更准确、用途更广泛。主要改进包括 增强了特征提取功能&#xff0c;从而可以更精确地捕捉细节以更…

啤酒在文学中的浪漫形象:精酿啤酒的诗意之旅

在文学的浩瀚星空中&#xff0c;啤酒并非仅仅是醉人的琼浆&#xff0c;它更是一种情感的载体&#xff0c;一种浪漫的符号。尤其是当提及Fendi Club精酿啤酒时&#xff0c;我们仿佛能闻到那从古老酒窖中飘出的馥郁香气&#xff0c;感受到它在文字间流淌的诗意与温情。 一、啤酒…

uniapp中检测应用更新的两种方式-升级中心之uni-upgrade-center-app

uniapp一个很是用的功能&#xff0c;就是在我们发布新版本的app后&#xff0c;需要提示用户进行app更新&#xff0c;并告知用户我们新版的app更新信息&#xff0c;以使得用户能及时使用上我们新开发的功能&#xff0c;提升用户的实用度和粘性。注意:这个功能只能在app端使用 效…

损失函数篇 | YOLOv10 更换损失函数之 MPDIoU | 《2023 一种用于高效准确的边界框回归的损失函数》

论文地址:https://arxiv.org/pdf/2307.07662v1.pdf 边界框回归(Bounding Box Regression,BBR)在目标检测和实例分割中得到了广泛应用,是目标定位的重要步骤。然而,对于边界框回归的大多数现有损失函数来说,当预测的边界框与真值边界框具有相同的长宽比,但宽度和高度的…

信号量SEM

前提 1.信号量的本质是一把计数器 2.申请信号本质就是预订资源 3.PV操作是原子的! 将一个公共资源当做整体访问-->锁 如果公共资源不当做整体使用&#xff0c;多进程可以并发的访问公共资源&#xff0c;但不是同一个区域&#xff0c;为了将资源均分&#xff0c;所以有了…

如何利用ChatGPT开发一个盈利的AI写作助手网站

3-1 整体介绍写作助手及原型展示说明 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正逐步改变我们的生活方式&#xff0c;特别是在内容创作领域。本文将详细介绍如何利用ChatGPT技术&#xff0c;开发一个能够生成高质量内容的AI写作助手网站&#xff…

埃及 Explained

古埃及&#xff0c;位于尼罗河畔的神秘文明&#xff0c;曾在北非的荒漠中繁荣昌盛。这个充满谜团的王国凭借其宏伟的成就和神秘的文化&#xff0c;数百年来吸引了无数人的好奇心。 埃及人创造了复杂的象形文字&#xff0c;建造了像吉萨大金字塔这样宏伟的建筑&#xff0c;并通…

字体文件压缩

技术点 npm、html、font-spider 实现原理 个人理解&#xff1a;先引入原先字体&#xff0c;然后重置字符为空&#xff0c;根据你自己填充文字、字符等重新生成字体文件&#xff0c;因此在引入的时候务必添加自己使用的文字、字符等&#xff01;&#xff01;&#xff01; 实…

k8s高级功能(系统升级)

版本升级 k8s由于1.23 到1.24底层变了&#xff0c;所以本次示例以1.22升到1.23 升级Master节点 &#xff08;在master节点执行&#xff09; 腾空节点 kubectl drain master --ignore-daemonsets 升级kubeadm yum install -y kubelet-1.23.17 kubeadm-1.23.17 kubectl-1.23.17…

【刷题6】一维前缀和、二维前缀和

目录 一、一维前缀和二、二维前缀和 一、一维前缀和 题目&#xff1a; 思路&#xff1a; 一、前缀和&#xff0c;时间复杂度O&#xff08;1&#xff09;&#xff0c;快速得到区间的值 二、预处理&#xff0c;公式——dp[i] dp[i-1] arr[i] 三、使用前缀和&#xff0c;根据…

使用小尺寸大模型和 Dify 清洗数据:Qwen 2.5 7B

本篇文章&#xff0c;我们聊聊如何使用最近发布的 Qwen 2.5 7B 模型来做日常低成本的数据清理工作。 写在前面 这个月好像比上个月还忙&#xff0c;去了很多地方&#xff0c;见了很多朋友。 之前云栖大会上说要写几篇 Qwen 相关的实践&#xff0c;一直没有时间&#xff0c;趁…

银河麒麟,apt 安装软件报错640Unknown Status

今天把银行麒麟的机器恢复出厂了&#xff0c;然后apt install 安装极其不稳定&#xff0c;故障现象如下图所示&#xff1a; 错误提示里面有&#xff1a; 640 Unknown Status [IP: 106.116.184.122 80] E: 无法下载 http://archive.kylinos.cn/kylin/KYLIN-ALL/pool/universe/f…

了解客户支持的人工智能:人工智能如何改变客户服务

作者&#xff1a;来自 Elastic Elastic Platform Team 我们都经历过这种情况&#xff1a;走进商店时&#xff0c;看到人工收银台排着长队&#xff0c;而所有自助收银台都是空的。这就是所谓的便捷工具并不那么便捷的情况。曾经&#xff0c;许多客户服务 “解决方案” 也处于这种…

腾讯云新开端口

检查防火墙设置 890 2024-09-30 20:47:18 netstat -tuln | grep 1213891 2024-09-30 20:47:49 ping 110.40.130.231892 2024-09-30 20:48:38 sudo firewall-cmd --zonepublic --add-port1213/tcp --permanent893 2024-09-30 20:48:51 sudo firewall-cmd --reload894 2024-…

加油站智能视频监控预警系统(AI识别烟火打电话抽烟) Python 和 OpenCV 库

加油站作为存储和销售易燃易爆油品的场所&#xff0c;是重大危险源之一&#xff0c;随着科技的不断发展&#xff0c;智能视频监控预警系统在加油站的安全保障方面发挥着日益关键的作用&#xff0c;尤其是其中基于AI的烟火识别、抽烟识别和打电话识别功能&#xff0c;以及其独特…

C++入门基础知识93(实例)——实例18【猴子吃桃问题】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于猴子吃桃问题的相关内容&#xff01; 关…

微信小程序处理交易投诉管理,支持多小程序

大家好&#xff0c;我是小悟 1、问题背景 玩过微信小程序生态的&#xff0c;或许就有这种感受&#xff0c;如果收到投诉单&#xff0c;不会及时通知到手机端&#xff0c;而是每天早上10:00向小程序的管理员及运营者推送通知。通知内容为截至前一天24时该小程序账号内待处理的交…