C# 泛型(Generic)

文章目录

  • 前言
  • 一、泛型的基本概念与实例展示
  • 二、泛型的特性与优势
  • 三、泛型方法
  • 四、泛型委托


前言

  泛型(Generic)允许将类或方法中编程元素的数据类型规范进行延迟编写,直到在程序实际使用这些类或方法的时候再去确定具体的数据类型。
在这里插入图片描述

一、泛型的基本概念与实例展示

  通过使用数据类型的替代参数来编写类或方法的规范,当编译器遇到类的构造函数被调用或者方法被执行时,它会依据实际传入的数据类型自动生成相应的代码来妥善处理该类型的数据。以下这个简单且直观的实例有助于我们深入理解这一概念:

using System;
using System.Collections.Generic;namespace GenericApplication
{// 定义泛型类 MyGenericArray<T>,这里的 T 就是类型参数,代表之后会确定的具体数据类型public class MyGenericArray<T>{private T[] array;// 构造函数,根据传入的大小创建对应类型的数组,注意这里数组类型是 T[],由类型参数决定public MyGenericArray(int size){array = new T[size + 1];}// 获取指定索引位置元素的方法,返回值类型也是 T,与数组元素类型一致public T getItem(int index){return array[index];}// 设置指定索引位置元素值的方法,接受的参数类型为 Tpublic void setItem(int index, T value){array[index] = value;}}class Tester{static void Main(string[] args){// 声明一个整型数组,此时将类型参数 T 指定为 int,创建了能处理整型数据的 MyGenericArray<int> 实例MyGenericArray<int> intArray = new MyGenericArray<int>(5);// 设置值,通过循环为整型数组元素赋值for (int c = 0; c < 5; c++){intArray.setItem(c, c * 5);}// 获取值,循环获取并输出整型数组元素的值for (int c = 0; c < 5; c++){Console.Write(intArray.getItem(c) + " ");}Console.WriteLine();// 声明一个字符数组,将类型参数 T 确定为 char,创建用于处理字符数据的 MyGenericArray<char> 实例MyGenericArray<char> charArray = new MyGenericArray<char>(5);// 设置值,按照一定规则为字符数组元素赋值for (int c = 0; c < 5; c++){charArray.setItem(c, (char)(c + 97));}// 获取值,循环获取并输出字符数组元素的值for (int c = 0; c < 5; c++){Console.Write(charArray.getItem(c) + " ");}Console.WriteLine();Console.ReadKey();}}
}

  当上述代码被编译和执行时,我们可以看到它先是对整型数组进行操作,输出了 0 5 10 15 20,随后对字符数组进行处理,输出了 a b c d e,完美展示了同一个泛型类如何依据不同的数据类型参数,灵活地处理各种类型的数据,体现了泛型的强大通用性。

二、泛型的特性与优势

  • 代码重用与类型安全保障
      泛型是一种能够极大限度地重用代码的技术手段。以往编写针对不同数据类型但功能相似的代码时,往往需要重复编写大量逻辑相似的代码,而泛型允许我们编写通用的逻辑框架,通过传入不同类型参数就能适用于多种类型的数据处理场景,避免了代码的冗余。同时,泛型还能有效地保护类型的安全。在编译阶段,编译器会基于泛型所确定的数据类型进行严格的类型检查,确保传入和使用的数据类型都是符合预期的,防止出现类型不匹配等错误,大大提高了程序的稳定性和健壮性。
  • 泛型集合类的应用
      .NET 框架类库在 System.Collections.Generic 命名空间中包含了众多新的泛型集合类。这些泛型集合类相较于 System.Collections 中的传统集合类有着显著优势,比如 List、Dictionary<TKey, TValue> 等。它们能够根据实际需求存储不同类型的数据,并且在进行元素操作、遍历等过程中,通过泛型机制保障类型安全,提高了集合操作的便利性和可靠性。我们可以使用这些泛型集合类来替代传统的非泛型集合类,以获得更好的编程体验和代码性能。
  • 多样化的泛型元素创建
      开发者不仅可以创建泛型类,还能够创建自己的泛型接口、泛型方法、泛型事件以及泛型委托等。例如,创建泛型接口可以定义一组通用的行为规范,让不同类型的类只要实现该接口并遵循其规范就能共享相同的接口调用逻辑;泛型方法则可以针对具体的某个操作逻辑,使其能适应多种数据类型的输入和处理需求;泛型事件能在事件触发和处理机制中融入泛型的灵活性;泛型委托更是可以依据不同类型参数来代表不同类型的方法签名,实现灵活的方法调用委托机制。
  • 泛型类的约束功能
      我们可以对泛型类进行约束,以此来访问特定数据类型的方法。通过添加约束条件,能够限定泛型类型参数必须满足某些要求,比如继承自某个特定类或者实现了某个接口等,这样在泛型类的内部代码中就能安全地调用那些满足约束条件的数据类型所特有的方法,进一步丰富了泛型在不同业务场景下的应用灵活性,使其能够更精准地适配各种复杂的编程需求。
  • 运行时反射获取类型信息
      关于泛型数据类型中使用的类型的信息,在程序运行时可以通过使用反射机制来获取。反射允许程序在运行阶段动态地获取类型的相关信息,包括泛型类型参数具体是什么类型等内容,这为一些需要动态处理不同类型数据或者根据运行时情况调整逻辑的高级编程场景提供了有力支持,例如动态创建泛型对象、根据类型信息调用不同的泛型方法等操作都可以借助反射来实现。

三、泛型方法

  在前面介绍的实例中,我们已经见识到了泛型类的使用,其实我们还可以通过类型参数来声明泛型方法。下面这个程序就很好地说明了泛型方法这一概念:

using System;
using System.Collections.Generic;namespace GenericMethodAppl
{class Program{// 定义泛型方法 Swap<T>,用于交换两个同类型的变量的值,T 为类型参数static void Swap<T>(ref T lhs, ref T rhs){T temp;temp = lhs;lhs = rhs;rhs = temp;}static void Main(string[] args){int a, b;char c, d;a = 10;b = 20;c = 'I';d = 'V';// 在交换之前显示值,输出初始的整型和字符型变量的值Console.WriteLine("Int values before calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values before calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);// 调用 swap 方法,分别传入整型和字符型的引用变量,实现对应类型变量值的交换Swap<int>(ref a, ref b);Swap<char>(ref c, ref d);// 在交换之后显示值,输出交换后的整型和字符型变量的值Console.WriteLine("Int values after calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values after calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);Console.ReadKey();}}
}

  示例中,Swap 方法通过类型参数 T 定义,可以接受任意相同类型的两个引用变量,然后在方法内部实现它们值的交换。无论是整型变量 a 和 b,还是字符型变量 c 和 d,都能通过调用这个泛型方法实现正确的交换操作,充分体现了泛型方法对不同数据类型的通用性,当代码被编译和执行后,会按照预期输出交换前后的变量值,展示了泛型方法的实际效果。

四、泛型委托

  泛型委托同样是泛型特性在 C# 中的重要应用形式之一,我们可以通过类型参数来定义泛型委托,使其能够代表不同类型的方法签名,进而实现灵活的方法调用委托机制。例如,以下是一个简单的泛型委托定义示例:

delegate T NumberChanger<T>(T n);

  在这个定义中,NumberChanger 就是一个泛型委托,它接受一个类型为 T 的参数,并返回类型同样为 T 的结果,通过不同的类型参数 T 的代入,就能代表不同类型输入输出要求的方法。下面通过一个完整的实例来演示泛型委托的使用:

using System;
using System.Collections.Generic;delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}static void Main(string[] args){// 创建委托实例,将 AddNum 方法关联到 NumberChanger<int> 委托类型上,此时委托可用于调用 AddNum 方法NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);// 同样创建委托实例,将 MultNum 方法关联到委托类型上NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);// 使用委托对象调用方法,通过 nc1 委托调用 AddNum 方法并传入参数 25nc1(25);Console.WriteLine("Value of Num: {0}", getNum());// 通过 nc2 委托调用 MultNum 方法并传入参数 5nc2(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}

  实例中,首先定义了 NumberChanger 泛型委托,然后创建了两个该委托类型的实例,分别关联了 AddNum 和 MultNum 两个方法,之后通过委托实例来调用相应的方法,实现了对不同方法的灵活调用和逻辑整合。当代码被编译和执行时,会按照方法的逻辑执行相应运算,并输出每次操作后的 num 值,展示了泛型委托如何依据不同的方法实现和参数类型,在程序中起到灵活调配方法调用的重要作用。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端小练习——星辰宇宙(JS没有上限!!!)

前言&#xff1a;在刚开始学习前端的时候&#xff0c;我们会学习到前端三件套中的JavaScript&#xff0c;可能那时候读者没有觉得JavaScript这个语言有多么的牛逼&#xff0c;本篇文章将会使用一个炫酷的案例来刷新你对JavaScript这个语言的认知与理解。 ✨✨✨这里是秋刀鱼不做…

【Python爬虫五十个小案例】爬取豆瓣电影Top250

博客主页&#xff1a;小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介&#xff1a;分享五十个Python爬虫小案例 &#x1fab2;前言 在这篇博客中&#xff0c;我们将学习如何使用Python爬取豆瓣电影Top250的数据。我们将使用requests库来发送HTTP请求&#xff0c;…

多目标优化算法——多目标粒子群优化算法(MOPSO)

Handling Multiple Objectives With Particle Swarm Optimization&#xff08;多目标粒子群优化算法&#xff09; 一、摘要&#xff1a; 本文提出了一种将帕累托优势引入粒子群优化算法的方法&#xff0c;使该算法能够处理具有多个目标函数的问题。与目前其他将粒子群算法扩展…

C++设计模式——Singleton单例模式

一、单例模式的定义 单例模式&#xff0c;英文全称Singleton Pattern&#xff0c;是一种创建型设计模式&#xff0c;它保证一个类在程序中仅有一个实例&#xff0c;并对外提供一个访问的该类实例的全局接口。 单例模式通常用于需要控制对象资源的开发场景&#xff0c;一个类…

Python学习35天

# 定义父类 class Computer: CPUNone MemoryNone diskNone def __init__(self,CPU,Memory,disk): self.disk disk self.Memory Memory self.CPU CPU def get_details(self): return f"CPU:{self.CPU}\tdisk:{self.disk}\t…

<项目代码>YOLOv8 停车场空位识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

mac下Gpt Chrome升级成GptBrowser书签和保存的密码恢复

cd /Users/自己的用户名/Library/Application\ Support/ 目录下有 GPT\ Chrome/ Google/ GptBrowser/ GPT\ Chrome 为原来的chrome浏览器的文件存储目录. GptBrowser 为升级后chrome浏览器存储目录 书签所在的文件 Bookmarks 登录账号Login 相关的文件 拷贝到GptBrow…

GB28181系列二:SIP信令

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、SIP报文介绍 二、SIP交互流程&#xff1a; 1、Session Model 2、Pager Model 3、SIP信令交互过程中的3个定义 三、媒体传输&#xff08;SDP和RTP&#xff09; 一、SIP报文介绍 这里将会介绍SIP…

ViSTa:一个包含4000多个视频和逐步描述的层次化数据集,用于评估VLMs在不同复杂性任务中的表现。

2024-11-22&#xff0c;由Google DeepMind和MATS机构创建的ViSTa数据集&#xff0c;为评估视觉语言模型&#xff08;VLMs&#xff09;在理解基于顺序的任务方面的能力提供了新的视角&#xff0c;这对于强化学习中的成本降低和安全性提升具有重要意义。 数据集地址&#xff1a;…

区块链:波场-TRON链

注意: 1、调试时请将所有的API地址都换成 https://api.trongrid.io 以免报错等问题 https://api.trongrid.io 主网 (Mainnet) 适用于生产环境 https://api.shasta.trongrid.io 测试网 (Shasta) 适用于开发者测试 https://nile.trongrid.io 测试网 (Nile) …

【适配】屏幕拖拽-滑动手感在不同分辨率下的机型适配

接到一个需求是类似下图的3D多房间视角&#xff0c;需要拖拽屏幕 问题 在做这种屏幕拖拽的时候发现&#xff0c;需要拖拽起来有跟手的感觉&#xff0c;会存在不同分辨率机型的适配问题。 即&#xff1a;美术调整好了机型1的手感&#xff0c;能做到手指按下顶层地板上下挪动&…

比特币libsecp256k1中safegcd算法形式化验证完成

1. 引言 比特币和其他链&#xff08;如 Liquid&#xff09;的安全性取决于 ECDSA 和 Schnorr 签名等数字签名算法的使用。Bitcoin Core 和 Liquid 都使用名为 libsecp256k1 的 C 库来提供这些数字签名算法&#xff0c;该库以其所运行的椭圆曲线命名。这些算法利用一种称为modu…

『VUE』elementUI dialog的子组件created生命周期不刷新(详细图文注释)

目录 1. 测试代码分析令人迷惑的效果 分析原因解决方法 如何在dialog中反复触发created呢?总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 主要是在做表单的时候想要有一个编辑表单在dialog弹窗中出现,同时dialog调用的封装的…

深入探讨 Redis 持久化机制:原理、配置与优化策略

文章目录 一、引言二、Redis持久化概述三、RDB&#xff08;Redis DataBase&#xff09;持久化1、RDB概念与工作原理2、RDB的配置选项3、RDB优化配置项4、RDB的优势与劣势 三、AOF&#xff08;Append-Only File&#xff09;持久化1、AOF概念与工作原理2、AOF的三种写回策略3、Re…

使用爬虫时,如何确保数据的准确性?

在数字化时代&#xff0c;数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性&#xff0c;并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后&#xff0c;需要对数据进行清洗&#xff0c;去除重复、无效和…

(计算机网络)期末

计算机网络概述 物理层 信源就是发送方 信宿就是接收方 串行通信--一次只发一个单位的数据&#xff08;串行输入&#xff09; 并行通信--一次可以传输多个单位的数据 光纤--利用光的反射进行传输 传输之前&#xff0c;要对信源进行一个编码&#xff0c;收到信息之后要进行一个…

111. UE5 GAS RPG 实现角色技能和场景状态保存到存档

实现角色的技能存档保存和加载 首先&#xff0c;我们在LoadScreenSaveGame.h文件里&#xff0c;增加一个结构体&#xff0c;用于存储技能相关的所有信息 //存储技能的相关信息结构体 USTRUCT(BlueprintType) struct FSavedAbility {GENERATED_BODY()//需要存储的技能UPROPERT…

Js-对象-04-Array

重点关注&#xff1a;Array String JSON BOM DOM Array Array对象时用来定义数组的。常用语法格式有如下2种&#xff1a; 方式1&#xff1a; var 变量名 new Array(元素列表); 例如&#xff1a; var arr new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据&#xff0…

【Flink-scala】DataStream编程模型之 窗口的划分-时间概念-窗口计算程序

DataStream编程模型之 窗口的划分-时间概念-窗口计算程序 1. 窗口的划分 1.1 窗口分为&#xff1a;基于时间的窗口 和 基于数量的窗口 基于时间的窗口&#xff1a;基于起始时间戳 和终止时间戳来决定窗口的大小 基于数量的窗口&#xff1a;根据固定的数量定义窗口 的大小 这…

Java代码操作Zookeeper(使用 Apache Curator 库)

1. Zookeeper原生客户端库存在的缺点 复杂性高&#xff1a;原生客户端库提供了底层的 API&#xff0c;需要开发者手动处理很多细节&#xff0c;如连接管理、会话管理、异常处理等。这增加了开发的复杂性&#xff0c;容易出错。连接管理繁琐&#xff1a;使用原生客户端库时&…