C#的泛型和匿名类型

一、C#的泛型简介

        泛型是一种允许你延迟编写类或方法中的数据类型规范,直到你在实际使用时才替换为具体的数据类型简单的说:泛型就是允许我们编写能够适用于任何数据类型的代码,而无需为每种特定类型重写相同的代码】(T是类型参数,起到站位符的作用,编译时被真正的类型替代)。

泛型的特性
序号泛型的特性说明
1泛型有助于开发人员最大限度的重用代码、保护类型的安全性和提高性能
2开发人员可以创建自己的【泛型接口】【泛型类】【泛型方法】【泛型事件】【泛型委托】
泛型的优点
序号泛型的优点说明
1类型安全泛型确保我们在实际代码中使用的都是正确的数据类型【可在编译时捕获到错误来确保类型安全】
2代码重用泛型可以让我们只用编写一次通用代码,就可用来处理各种不同数据类型(如各种方法重载)
3提高性能

泛型避免了不必要的类型转换【没有装箱拆箱的消耗】,使得程序运行更快

装箱和取消装箱 - C# | Microsoft Learn

C#基础:理解装箱与拆箱

C# 装箱和拆箱

4灵活性可以创建泛型接口、类、方法和委托内容,可处理我们选择的任何类型
泛型的参数类型约束

《1》【泛型约束】主要是用于告知编译器类型参数必须具备的功能在没有任何约束的情况下,类型参数可以是任何类型。 编译器只能假定 System.Object 的成员,它是任何 .NET 类型的最终基类);

《2》使用泛型约束的原因是【约束指定类型参数的功能和预期】( 声明这些约束意味着你可以使用约束类型的操作和方法调用)

序号泛型约束说明
1where T : struct表示类型参数必须是不可为 null 的值类型; 由于所有值类型都具有可访问的无参数构造函数(无论是声明的还是隐式的),因此 struct 约束表示 new() 约束,并且不能与 new() 约束结合使用。 struct 约束也不能与 unmanaged 约束结合使用
2where T : class类型参数必须是引用类型。 此约束还应用于任何类、接口、委托或数组类型
3where T : class?类型参数必须是可为 null 或不可为 null 的引用类型。 此约束还应用于任何类、接口、委托或数组类型(包括记录)
4where T : notnull类型参数必须是不可为 null 的类型。 参数可以是不可为 null 的引用类型,也可以是不可为 null 的值类型。
5where T : unmanaged类型参数必须是不可为 null 的非托管类型。 unmanaged 约束表示 struct 约束,且不能与 struct 约束或 new() 约束结合使用。
6where T : new()类型参数必须具有公共无参数构造函数。 与其他约束一起使用时,new() 约束必须最后指定。 new() 约束不能与 struct 和 unmanaged 约束结合使用。
7where T :<基类名>类型参数必须是指定的基类或派生自指定的基类。 在可为 null 的上下文中,T 必须是从指定基类派生的不可为 null 的引用类型。
8where T :<基类名>?类型参数必须是指定的基类或派生自指定的基类。 在可为 null 的上下文中,T 可以是从指定基类派生的可为 null 或不可为 null 的类型。
9where T :<接口名>类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在的可为 null 的上下文中,T 必须是实现指定接口的不可为 null 的类型
10where T :<接口名>?类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在可为 null 的上下文中,T 可以是可为 null 的引用类型、不可为 null 的引用类型或值类型。 T 不能是可为 null 的值类型
11where T : U为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。 在可为 null 的上下文中,如果 U 是不可为 null 的引用类型,T 必须是不可为 null 的引用类型。 如果 U 是可为 null 的引用类型,则 T 可以是可为 null 的引用类型,也可以是不可为 null 的引用类型
12where T : default重写方法或提供显式接口实现时,如果需要指定不受约束的类型参数,此约束可解决歧义。 default 约束表示基方法,但不包含 class 或 struct 约束。 有关详细信息,请参阅default约束规范建议
13where T : allows ref struct此反约束声明 T 的类型参数可以是 ref struct 类型。 该泛型类型或方法必须遵循 T 的任何实例的引用安全规则,因为它可能是 ref struct

某些约束是互斥的,而某些约束必须按指定顺序排列:

《1》最多可应用 structclassclass?notnull 和 unmanaged 约束中的一个,则它必须是为该类型参数指定的第一个约束;

《2》基类约束(where T : Base 或 where T : Base?)不能与 structclassclass?notnull 或 unmanaged 约束中的任何一个结合使用;

《3》无论哪种形式,都最多只能应用一个基类约束。 如果想要支持可为 null 的基类型,请使用 Base?;

《4》不能将接口不可为 null 和可为 null 的形式命名为约束;

《5》new() 约束不能与 struct 或 unmanaged 约束结合使用。 如果指定 new() 约束,则它必须是该类型参数的最后一个约束。 反约束(如果适用)可以遵循 new() 约束;

《6》default 约束只能应用于替代或显式接口实现。 它不能与 struct 或 class 约束结合使用;

《7》allows ref struct 反约束不能与 class 或 class? 约束结合使用;

《8》allows ref struct 反约束必须遵循该类型参数的所有约束;

二、C#的泛型和匿名类型使用

 2.1、泛型类示例

//泛型类定义【基础】
修饰符 class 类名<T>
{类代码
}

        泛型引入了类型参数用来充当数据类型的占位符,如下是一个可用于任何数据类型的泛型类和方法示例:

/// <summary>
/// 箱子泛型类
/// </summary>
/// <typeparam name="T">T是类型参数,可以是C#支持的所有数据类型(如:string,int,double,bool等)</typeparam>
internal class Box<T>
{public T Value { get; set; }public Box(T value){this.Value = value;    }public void Print(){Console.WriteLine($"【{Value}】属于【{Value?.GetType()}】类型");}}//Class_end/// <summary>
/// 测试【箱子泛型类】的部分示例
/// </summary>
private static void TestBox()
{Console.WriteLine("---创建一个存放string数据的箱子---");Box<string> strBox = new Box<string>("字符串箱子");strBox.Print();Console.WriteLine("---创建一个存放Int数据的箱子---");Box<int> intBox = new Box<int>(666);intBox.Print();Console.WriteLine("---创建一个存放Double数据的箱子---");Box<double> doubleBox = new Box<double>(777.88888);doubleBox.Print();}

运行结果如下:【可以看到我们创建的泛型Box类可以创建各种数据类型的内容并打印出来】

2.2、泛型方法示例

 //泛型方法定义修饰符 void 方法名称<类型参数>(类型参数 t){}//多泛型方法修饰符 void 方法名称<类型参数1,类型参数2>(类型参数1 left, 类型参数2 Right){}//带约束的泛型方法修饰符 类型参数 方法名称<类型参数>(类型参数 left, 类型参数 Right) where 类型参数 : 约束{}
  //定义一个泛型方法,且实现泛型的数学运算internal class TestMethod{public static T Add<T>(T left, T Right) where T : INumber<T>{return left + Right;}}//Class_end//测试泛型方法private static void Test(){     int res = TestMethod.Add(5,6);Console.WriteLine($"5+6={res}");double res2 = TestMethod.Add(5.55, 6.32);Console.WriteLine($"5.55+6.32={res2}");}

运行结果如下:

2.3、泛型接口示例

//泛型接口定义
修饰符 interface I接口名称<类型参数> 
{类型参数 字段名称{ get; }void 方法名称1(类型参数 t);  类型参数 方法名称2(); 
}

        使用泛型接口可在不同的类型中强制实施类型安全行为,且在不同类型之间强制实施一致行为,同时使代码保持灵活且可重用。

System.Collections.Generic 命名空间(C#所有泛型集合的接口和类) | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/api/system.collections.generic?view=net-9.0        IComparer<in T>接口表示具体的比较方法;实现创建PeopleComparer继承IComparer泛型接口对People的年龄比较排序示例:

--定义两个数据类型比较的接口IComparer
public interface IComparer<in T>
{int Compare(T? x, T? y);
}internal class People {public string? Name { get; set; }public int Age { get; set; }}//Class_end--继承 IComparer接口并实现具体的比较方法
internal class PeopleComparer : IComparer<People>
{public int Compare(People? x, People? y){return x.Age.CompareTo(y.Age);}
}//Class_end//测试泛型接口
private static void TestGenericInterface()
{var Peoples = new List<People>{new People{Name="张三",Age=26 },new People{Name="李四",Age=29 },new People{Name="王五",Age=28 }};Peoples.Sort(new PeopleComparer());// Peoples.Sort(new PeopleComparer());foreach (var people in Peoples) { Console.WriteLine($"【{people.Name}】【{people.Age}】"); }
}
协变和逆变
序号协变和逆变说明
1协变

允许将更具体的类型(派生类型)分配给更常规的类型(基类型)

//示例【将更具体的string类型转为object类】
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
2逆变

允许将更常规的类型(基类型)分配给更具体的类型(派生类型)

 //示例【将通用的object类型转换为具体的stirng类型】Action<object> handleObject = obj => Console.WriteLine(obj);Action<string> handleString = handleObject;

1、使用泛型类型时,协变和逆变允许灵活性,尤其是在将一种类型分配给另一种类型时。 它们有助于确保某些方案中相关类型之间的兼容性。

2、读取数据(如循环访问集合)时,协变非常有用。 在写入或处理数据(如将参数传递给方法)时,逆变非常有用。

协变和逆变 - C# | Microsoft Learn

2.4、泛型委托示例

namespace TestConsole
{//定义泛型委托public delegate T1 MyDel<T1,T2>(T2 t2);internal class TestDel{public TestDel(){//注册委托1方法notify += MsgNotify;//注册委托2方法sendMsg += SendMsg;}//声明泛型委托1public MyDel<string,bool> notify;//编写泛型委托1的需要调用的方法private string MsgNotify(bool status){string res = "";if (status){Console.WriteLine($"---泛型委托1执行:状态是【{status}】---");res = "执行泛型委托1完成";}return res;}//声明泛型委托2public MyDel<int, string> sendMsg;//编写泛型委托2的需要调用的方法private int SendMsg(string msg){int res= 0;if (!string.IsNullOrEmpty(msg) && msg.Contains("sg")){var tmp = msg.Split("sg");Console.WriteLine($"---泛型委托2执行:【{tmp[1]}】开始发送到加密服务器---");res = 222;}return res;}}
}
 /// <summary>/// 测试泛型委托/// </summary>private static void TestGenericDelegate(){TestConsole.TestDel testDel= new TestConsole.TestDel();//使用委托1string res1 = testDel.notify(true);Console.WriteLine($"使用委托1的结果是【{res1}】\n");//使用委托2int res2 = testDel.sendMsg("sg你好,我是客户端");Console.WriteLine($"使用委托2的结果是【{res2}】\n");}

 执行结果:

 2.5、匿名类型示例

匿名类型特点
序号匿名类型特点【匿名类型主要用于临时数据结构,定义完整类是不必要的
1匿名类型是使用 new 运算符和对象初始值设定项创建的
2匿名类通常使用隐式类型变量var声明
3它们通常用于语言集成查询(LINQ)中,以返回对象的部分属性

注意:

《1》匿名类型允许创建具有只读属性的对象,且不用定义类【编译器为类型生成名称,且该名称在源码中无法访问;其中编译器会自行确定每个属性的类型】;

《2》匿名类型不能用作方法参数或返回类型;

《3》匿名类型只适用于方法范围内创建临时数据结构;

//创建匿名对象
var tmp = new { Name = "匿名类型", msg = "测试" };
Console.WriteLine($"{tmp.Name} {tmp.msg}");
//创建匿名对象数组 
var tmpObjArray = new[]{new { Name="AB床垫",Price=1600 },new { Name="鼠标",Price=169},new { Name="键盘",Price=199 },new { Name="显示器",Price=699 },};//使用linq语法对内容进行过滤var filterRes = from obj in tmpObjArraywhere obj.Price >= 200select new { obj.Name, obj.Price };foreach (var obj in filterRes){Console.WriteLine($"【{obj.Name}】【{obj.Price}】");}
匿名类型和元组类型比较
序号功能 / 特点匿名类型元组类型
1类型引用类型 (class值类型 (struct
2自定义成员名称支持支持        
3析构不支持支持
4表达式树支持支持不支持
在匿名类型和元组类型之间进行选择 - .NET | Microsoft Learn

三、参考资料

泛型类型参数 - C# | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-type-parametersNew 约束 - C# reference | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/new-constraint泛型类和方法 - C# | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/fundamentals/types/generics委托和事件简介 - C# | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/delegates-overview

C# - 泛型:初学者的友好引导 - C# 高级教程 - W3schoolshttps://w3schools.tech/zh-cn/tutorial/csharp/csharp_generics 

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

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

相关文章

日语面试ai助手推荐:高效备考并应对日语面试难题

在准备日语面试的路上&#xff0c;你是否时常感到力不从心&#xff1f;每到模拟面试环节&#xff0c;总怕自己答非所问、用语不地道&#xff0c;或是紧张到脑子一片空白。查找资料时&#xff0c;面对海量的日语问答、面试范本和专业术语&#xff0c;常常分不清轻重缓急&#xf…

【63 Pandas+Pyecharts | 泡泡玛特微博热搜评论数据分析可视化】

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. Pandas数据处理2.1 读取数据2.2 数据信息2.3 数据去重2.4 数据去空2.5 时间处理2.6 性别处理2.7 评论内容处理 &#x1f3f3;️‍&#x1f308; 3. Pyecharts数据可视化3.1 用户评论IP分…

python-最长无重复子数组

最长无重复子数组 描述代码实现 描述 给定一个长度为n的数组arr&#xff0c;返回arr的最长无重复元素子数组的长度&#xff0c;无重复指的是所有数字都不相同。 子数组是连续的&#xff0c;比如[1,3,5,7,9]的子数组有[1,3]&#xff0c;[3,5,7]等等&#xff0c;但是[1,3,7]不是…

探索 MySQL 缓存机制:提升数据库读取性能的有效策略

在现代应用中,数据库的读取性能是影响用户体验和系统响应速度的关键因素。当应用程序面临高并发读请求时,直接访问磁盘的开销会成为瓶颈。为了应对这一挑战,MySQL 引入了多种缓存机制,旨在减少磁盘 I/O,加快数据检索速度。 理解并合理利用这些缓存机制,是提升 MySQL 数据…

深度学习-164-MCP技术之开发本地MCP服务器和异步客户端

文章目录 1 概念1.1 MCP1.2 准备数据接口2 开发MCP服务器2.1 server.py2.1.1 @mcp.resource2.1.2 @mcp.tool()2.1.3 @mcp.prompt()2.2 调试模式启动mcp-server2.2.1 资源2.2.2 工具2.2.3 提示词3 开发MCP客户端3.1 调用工具client_tool3.2 获取提示client_prompt3.3 读取资源cl…

第八十一篇 大数据开发基础:队列数据结构详解与实战应用(附生活化案例)

在大数据开发的庞大体系中&#xff0c;队列&#xff08;Queue&#xff09; 作为基础数据结构之一&#xff0c;其重要性不言而喻。它不仅是构建高效数据管道的核心组件&#xff0c;更是实现异步处理、流量削峰、任务调度的关键技术。本文将深入解析队列的原理&#xff0c;结合生…

linux操作命令(最常用)

一、文件与目录操作 命令作用常用参数示例ls列出目录内容ls -l&#xff08;详细列表&#xff09; ls -a&#xff08;显示隐藏文件&#xff09;cd切换目录cd ~&#xff08;回家目录&#xff09; cd ..&#xff08;返回上级&#xff09;pwd显示当前路径-cp复制文件/目录cp -r di…

22.react和next.js、SSR与CSR的比较

SSR 和 CSR 的区别 &#x1f538; 示例说明 SSR 流程&#xff08;Next.js 支持&#xff09;&#xff1a; 1. 用户请求页面 2. 服务端生成 HTML&#xff08;含内容&#xff09; 3. 浏览器收到渲染好的页面 // SSR 页面&#xff08;默认行为&#xff09; - app/page.tsx export…

全栈加速:FrankenPHP 架构原理与实战案例

在当今云原生与微服务大行其道的时代&#xff0c;PHP 应用面临着「冷启动延迟高」「进程管理复杂」「性能瓶颈难以突破」等痛点。 FrankenPHP 正是为了解决这些问题而生&#xff1a;它将 Caddy 服务器与 PHP 运行时深度融合&#xff0c;内嵌 Let’s Encrypt 自动 HTTPS、支持 …

Android开发中的适配

目录 一:分辨率适配 1.1概念 1.2关键策略 二:多尺寸适配 2.1概念 2.2关键策略 三:多平台多版本适配 3.1Android系统版本迭代 3.2 关键策略 Android开发中的屏幕适配与多版本适配 在Android开发中,屏幕适配和多版本适配是确保应用在各种设备上都能良好运行和显示的关键。这不…

【MySQL基础篇】MySQL中的算术运算符和比较运算符

精选专栏链接 &#x1f517; MySQL技术笔记专栏Redis技术笔记专栏大模型搭建专栏Python学习笔记专栏深度学习算法专栏 欢迎订阅&#xff0c;点赞&#xff0b;关注&#xff0c;每日精进1%&#xff0c;共攀技术高峰 更多内容持续更新中&#xff01;希望能给大家带来帮助~ &…

FFmpeg推流实战30秒速成

FFmpeg windows 7.1.1下载地址 FFmpeg 推流方法 FFmpeg 是一个强大的多媒体处理工具&#xff0c;支持将视频和音频推流到各种流媒体服务器&#xff08;如 RTMP、RTSP、HLS 等&#xff09;。以下是几种常见的推流方法。 推流到 RTMP 服务器 RTMP&#xff08;Real-Time Messa…

74HC595功能介绍及代码驱动

一、引脚描述 QA~QH(15,1~7脚):数据输出引脚 QH1(9脚):移位寄存器串行数据输出脚,当移位寄存器中的数据多余8位时,最先进入的那位被挤出去,一般级联使用,接下一个74HC595 G(13脚):输出使能引脚,低电平使能 RCK(12脚):存储寄存器输入数据使能引脚,上升沿时…

AntV G 入门教程

下面是 AntV G&#xff08;以下简称 G&#xff09;的中文入门与核心 API 教程&#xff0c;涵盖从画布创建、图形绘制到事件与动画等常用方法&#xff0c;每个 API 均附带完整示例代码。示例引用自官方“Getting Started”指南 ([g.antv.antgroup.com][1])。 一、安装与引入 #…

短视频矩阵什么意思?

短视频矩阵是指通过布局多个短视频账号&#xff0c;形成协同运营的账号体系&#xff0c;以实现流量聚合、品牌曝光或商业变现的策略。其核心逻辑是利用不同账号的定位、内容风格或受众群体&#xff0c;构建互补的流量网络&#xff0c;而非单一账号的独立运营。 核心特点与作用&…

Linux 日志查看和分析

Linux 日志是系统运行状态的重要记录&#xff0c;包含了系统启动、服务运行、用户操作、安全事件等关键信息&#xff0c;对于故障排查、安全审计和系统维护至关重要。 故障排查&#xff1a;定位系统崩溃、服务异常的根本原因&#xff08;如服务启动失败、硬件故障&#xff09;…

一篇文章快速学会HTML

一篇文章快速学会HTML 注&#xff1a;适合有一定编程基础的来快速掌握HTML 超文本标记语言 超文本&#xff1a;文本&#xff0c;声音&#xff0c;图片&#xff0c;视频&#xff0c;表格&#xff0c;链接 标记&#xff1a;许多的标签组成 HTML页面是运行到浏览器上的 HTML…

智能混合检索DeepSearch

智能混合检索 DeepSearch 是一款自主研发的大规模分布式搜索引擎&#xff0c;提供一站式智能搜索解决方案。系统内置多种行业专属的查询语义理解能力&#xff0c;融合语义 ORC 模型、文本向量模型、图像/视频向量模型、大语言模型&#xff08;LLM&#xff09;、分词器以及机器学…

【Docker基础】Docker镜像管理:docker tag详解

目录 1 Docker镜像标签基础概念 1.1 什么是Docker镜像标签 1.2 镜像标识的三要素 2 docker tag命令详解 2.1 命令基本语法 2.2 命令工作原理 2.3 常用操作示例 3 标签管理的实践示例 3.1 标签命名规范 3.2 多标签策略 3.3 latest标签的合理使用 4 标签与镜像仓库的…

AI时代个人IP的重塑与机遇 | 创客匠人

2025年作为AI应用爆发元年&#xff0c;正悄然改写个人IP的打造逻辑。AI不会取代IP&#xff0c;却会淘汰不懂得与AI共生的创作者。 AI重构IP运营的三大机遇 内容生产效率提升&#xff1a;传统模式下需2-3天打磨的深度文章&#xff0c;AI输入关键词后半小时即可完成初稿&#xf…