图解C#高级教程(三):泛型
本讲用许多代码示例介绍了 C# 语言当中的泛型,主要包括泛型类、接口、结构、委托和方法。
文章目录
- 1. 为什么需要泛型?
- 2. 泛型类的定义
- 2.1 泛型类的定义
- 2.2 使用泛型类创建变量和实例
- 3. 使用泛型类实现一个简单的栈
- 3.1 类型参数的约束
- 3.2 Where 子句
- 3.3 约束类型和次序
- 4. 泛型方法
- 5. 泛型结构
- 6. 泛型委托
- 7. 泛型接口
1. 为什么需要泛型?
在之前的教程中,我们使用的自定义类都是具有具体的类型。如果对类型进行抽象,该类型就是泛型,即一种广泛的类型。例如,我们有一个水果篮,水果篮中可以放不同种类的水果,例如苹果、香蕉、菠萝等等。这个水果篮可以放置的水果就是一种泛型,水果可以代指很多不同种类的水果。
为什么需要泛型?泛型的好处之一就是能够提高代码的复用性。例如,我们有一种栈数据结构,栈当中的类型可以是 int
、float
等等,但很多操作都是相同的:入栈、出栈、获取栈大小,所不同的是数据类型的不同。利用泛型,我们只需要实现一套代码,而不需要针对不同的数据类型分别实现各自的一套栈代码。
在 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# 语言当中的泛型,主要包括泛型类、接口、结构、委托和方法。 文章目录 1. 为什么需要泛型?2. 泛型类的定义2.1 泛型类的定义2.2 使用泛型类创建变量和实例 3. 使用泛型类实现一个简单的栈3.1 类型参数的约束3.2 Where 子句3…...

240930_CycleGAN循环生成对抗网络
240930_CycleGAN循环生成对抗网络 CycleGAN,也算是笔者记录GAN生成对抗网络的第四篇,前三篇可以跳转 240925-GAN生成对抗网络-CSDN博客 240929-DCGAN生成漫画头像-CSDN博客 240929-CGAN条件生成对抗网络-CSDN博客 在第三篇中,我们采用了p…...
ide 使用技巧与插件推荐
ide 使用技巧与插件推荐 一、IDE 使用技巧 1. 快捷键 掌握常用快捷键: Windows: 使用 Ctrl、Alt 和 Shift 的组合。 Mac: 使用 Cmd、Option 和 Shift。 常用快捷键示例: VS Code: Ctrl P: 快速打开文件。 Ctrl Shift P: 打开命令面板。 Ctrl /…...
【node】 cnpm|npm查看、修改镜像地址操作 换源操作
【node】 cnpm|npm查看、修改镜像地址操作 换源操作 安装完node后 npm 1.查看当前npm信息 npm -v2.查看当前的镜像源 npm config get registry3.如果需要淘宝镜像源,修改当前的镜像源为淘宝镜像源 registry https://registry.npm.taobao.org弃用 npm config se…...

大数据-152 Apache Druid 集群模式 配置启动【下篇】 超详细!
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&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类,并加载自定义fxml 前提步骤1. 普通类继承Application2. 实现main方法3. 写一个controller4. 写一个fxml文件5. 写start方法加载fxml6. 具体代码7. 运行即可 前提 使用自带javafx的jdk,这里使用的是jdk1.834ÿ…...
数据结构之——单循环链表和双向循环链表
一、单循环链表的奥秘 单循环链表是一种特殊的链表结构,它在数据结构领域中具有重要的地位。其独特的循环特性使得它在某些特定的应用场景中表现出强大的优势。 (一)结构与初始化 单循环链表的结构由节点组成,每个节点包含数据域…...
Git Stash: 管理临时更改的利器
Git 是一个非常强大的版本控制系统,它不仅帮助我们管理代码的版本,还提供了许多实用的功能来优化我们的工作流程。今天,我们要介绍的是 Git 中的一个非常实用的功能——git stash。 什么是 Git Stash? 在开发过程中,…...

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

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

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

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

Hive SQL业务场景:连续5天涨幅超过5%股票
一、需求描述 现有一张股票价格表 dwd_stock_trade_dtl 有3个字段分别是: 股票代码(stock_code), 日期(trade_date), 收盘价格(closing_price) 。 请找出满足连续5天以上(含)每天上涨超过5%的股票,并给出连续满足…...

Java 如何从图片上提取文字
生活中我们可能会遇到想从图片上直接复制上边的文字,该如何获取呢,接下来看看如何使用Java程序实现从图片中读取文字。 实现过程 1、引入Tess4J 依赖 <!--Tess4J 依赖--> <dependency><groupId>net.sourceforge.tess4j</groupId…...
C#进阶-读写Excel常用框架及其使用方式
目录 一、MiniExcel开源框架(推荐) 1、写/导出 方式一 方式二 多表创建 更改配置 特性使用 CSV尾行新增行 CSV、XLSX互转 2、读/导入 简单示例 二、NPOI开源框架 一、MiniExcel开源框架(推荐) 添加NuGet包MiniExcel…...
Python爬虫lxml模块安装导入和xpath基本语法
lxml模块是Python的一个解析库,主要用于解析HTML和XML文件。 一、安装导入 使用包管理器安装,在cmd下或编辑器下的控制台,运行: pip install lxml 导入: from lxml import etree 二、xpath基础知识 XPath&#…...
python魔法(python高级magic方法进阶)
python特殊方法(magic方法也叫魔术方法) 魔法方法是python的内置函数,一般以双下划线开头和结尾, 构造和初始化 每个人都知道一个最基本的魔术方法, init 。 通过此方法我们可以定义一个对象的初始操作。 然而,当我调用 x S…...

【论文笔记】Flamingo: a Visual Language Model for Few-Shot Learning
🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: Flamingo: a Visual Langu…...
问:JAVA阻塞队列实现类及最佳实践?
在多线程编程中,阻塞队列作为一种关键的数据结构,为线程间安全、高效的数据交换提供了重要支持。Java的java.util.concurrent包中提供了多种阻塞队列的实现,每种实现都有其独特的特点和适用场景。 一、阻塞队列实现类 以下是Java中Blocking…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...