当前位置: 首页 > 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…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...