当前位置: 首页 > news >正文

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

本讲用许多代码示例介绍了 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; }// Constructorpublic 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# 语言当中泛型{类、接口、结构、委托、方法}的用法。

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

相关文章:

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

本讲用许多代码示例介绍了 C# 语言当中的泛型&#xff0c;主要包括泛型类、接口、结构、委托和方法。 文章目录 1. 为什么需要泛型&#xff1f;2. 泛型类的定义2.1 泛型类的定义2.2 使用泛型类创建变量和实例 3. 使用泛型类实现一个简单的栈3.1 类型参数的约束3.2 Where 子句3…...

240930_CycleGAN循环生成对抗网络

240930_CycleGAN循环生成对抗网络 CycleGAN&#xff0c;也算是笔者记录GAN生成对抗网络的第四篇&#xff0c;前三篇可以跳转 240925-GAN生成对抗网络-CSDN博客 240929-DCGAN生成漫画头像-CSDN博客 240929-CGAN条件生成对抗网络-CSDN博客 在第三篇中&#xff0c;我们采用了p…...

ide 使用技巧与插件推荐

ide 使用技巧与插件推荐 一、IDE 使用技巧 1. 快捷键 掌握常用快捷键&#xff1a; Windows: 使用 Ctrl、Alt 和 Shift 的组合。 Mac: 使用 Cmd、Option 和 Shift。 常用快捷键示例&#xff1a; VS Code: Ctrl P: 快速打开文件。 Ctrl Shift P: 打开命令面板。 Ctrl /…...

【node】 cnpm|npm查看、修改镜像地址操作 换源操作

【node】 cnpm|npm查看、修改镜像地址操作 换源操作 安装完node后 npm 1.查看当前npm信息 npm -v2.查看当前的镜像源 npm config get registry3.如果需要淘宝镜像源&#xff0c;修改当前的镜像源为淘宝镜像源 registry https://registry.npm.taobao.org弃用 npm config se…...

大数据-152 Apache Druid 集群模式 配置启动【下篇】 超详细!

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

IDE 使用技巧与插件推荐全面指南

目录 目录 常用IDE概述 Visual Studio Visual Studio Code IntelliJ IDEA PyCharm Eclipse IDE 使用技巧 通用技巧 Visual Studio 专属技巧 Visual Studio Code 专属技巧 IntelliJ IDEA 专属技巧 插件推荐 Visual Studio 插件 Visual Studio Code 插件 IntelliJ…...

java-快速将普通main类变为javafx类,并加载自定义fxml

java-快速将普通main类变为javafx类&#xff0c;并加载自定义fxml 前提步骤1. 普通类继承Application2. 实现main方法3. 写一个controller4. 写一个fxml文件5. 写start方法加载fxml6. 具体代码7. 运行即可 前提 使用自带javafx的jdk&#xff0c;这里使用的是jdk1.834&#xff…...

数据结构之——单循环链表和双向循环链表

一、单循环链表的奥秘 单循环链表是一种特殊的链表结构&#xff0c;它在数据结构领域中具有重要的地位。其独特的循环特性使得它在某些特定的应用场景中表现出强大的优势。 &#xff08;一&#xff09;结构与初始化 单循环链表的结构由节点组成&#xff0c;每个节点包含数据域…...

Git Stash: 管理临时更改的利器

Git 是一个非常强大的版本控制系统&#xff0c;它不仅帮助我们管理代码的版本&#xff0c;还提供了许多实用的功能来优化我们的工作流程。今天&#xff0c;我们要介绍的是 Git 中的一个非常实用的功能——git stash。 什么是 Git Stash&#xff1f; 在开发过程中&#xff0c;…...

ELK--收集日志demo

ELK--收集日志demo 安装ELK日志收集配置启动容器springboot配置测试 之前项目多实例部署的时候&#xff0c;由于请求被负载到任意节点&#xff0c;所以查看日志是开多个终端窗口。后来做了简单处理&#xff0c;将同一项目的多实例日志存入同一个文件&#xff0c;由于存在文件锁…...

Redis的主要特点及运用场景

Redis的主要特点及运用场景 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的高性能键值对&#xff08;key-value&#xff09;数据库。它支持多种类型的数据结构&#xff0c;如字符串&#xff08;strings&#xff09;、散列&#xff08;hashes&…...

与我免费ai书童拆解《坚持》创作历程

插科打诨的海侃胡闹&#xff0c;调侃舒展《坚持》诗创的灵魂盛宴之旅。 (笔记模板由python脚本于2024年09月30日 19:11:42创建&#xff0c;本篇笔记适合喜欢python和诗歌的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#x…...

昇思MindSpore进阶教程--下沉模式

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 正文开始 昇腾芯片集成了AICORE和AICPU等…...

Hive SQL业务场景:连续5天涨幅超过5%股票

一、需求描述 现有一张股票价格表 dwd_stock_trade_dtl 有3个字段分别是&#xff1a; 股票代码(stock_code), 日期(trade_date)&#xff0c; 收盘价格(closing_price) 。 请找出满足连续5天以上&#xff08;含&#xff09;每天上涨超过5%的股票&#xff0c;并给出连续满足…...

Java 如何从图片上提取文字

生活中我们可能会遇到想从图片上直接复制上边的文字&#xff0c;该如何获取呢&#xff0c;接下来看看如何使用Java程序实现从图片中读取文字。 实现过程 1、引入Tess4J 依赖 <!--Tess4J 依赖--> <dependency><groupId>net.sourceforge.tess4j</groupId…...

C#进阶-读写Excel常用框架及其使用方式

目录 一、MiniExcel开源框架&#xff08;推荐&#xff09; 1、写/导出 方式一 方式二 多表创建 更改配置 特性使用 CSV尾行新增行 CSV、XLSX互转 2、读/导入 简单示例 二、NPOI开源框架 一、MiniExcel开源框架&#xff08;推荐&#xff09; 添加NuGet包MiniExcel…...

Python爬虫lxml模块安装导入和xpath基本语法

lxml模块是Python的一个解析库&#xff0c;主要用于解析HTML和XML文件。 一、安装导入 使用包管理器安装&#xff0c;在cmd下或编辑器下的控制台&#xff0c;运行&#xff1a; pip install lxml 导入&#xff1a; from lxml import etree 二、xpath基础知识 XPath&#…...

python魔法(python高级magic方法进阶)

python特殊方法(magic方法也叫魔术方法) 魔法方法是python的内置函数&#xff0c;一般以双下划线开头和结尾&#xff0c; 构造和初始化 每个人都知道一个最基本的魔术方法&#xff0c; init 。 通过此方法我们可以定义一个对象的初始操作。 然而&#xff0c;当我调用 x S…...

【论文笔记】Flamingo: a Visual Language Model for Few-Shot Learning

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Flamingo: a Visual Langu…...

问:JAVA阻塞队列实现类及最佳实践?

在多线程编程中&#xff0c;阻塞队列作为一种关键的数据结构&#xff0c;为线程间安全、高效的数据交换提供了重要支持。Java的java.util.concurrent包中提供了多种阻塞队列的实现&#xff0c;每种实现都有其独特的特点和适用场景。 一、阻塞队列实现类 以下是Java中Blocking…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...