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

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...