当前位置: 首页 > news >正文

【C#学习笔记】数据类中常用委托及接口——以List<T>为例

在这里插入图片描述

文章目录

  • List\<T\>/LinkedList \<T\>为什么是神?(泛型为什么是神)
  • 一些常见,通用的委托和接口
    • `Comparison`
    • `Enumerator`


List<T>/LinkedList <T>为什么是神?(泛型为什么是神)

List<T>为什么是神?在谈论这个问题之前,我想先说说其他数据表结构相较于List<T>究竟差在了哪里……

首先是HashTable

本身呢就被Dictionary<TKey,TValue>完爆,HashTable既不是线程安全的,也不是类型安全的,虽然提供了Synchronized()方法可以获取线程安全的类型,以为自己是个哈希表就可以为所欲为了,但这种挑战神的行为导致最终降下了神罚,最后几乎被HasSet<T>所取代。HashSet<T>的Contains方法复杂度是O(1),List<T>的Contains方法复杂度是O(n)。HashSet还是线程安全和类型安全的。而且HashSet<T>是专门设计用来做集合运算(取交集,并集等),所以提供了UnionWith、IntersectWith等方法。无论从方方面面来看HashTable都比不上HashSet<T>

然后是Array数组类型

竟然还胆敢在神的面前跳脚,仗着自己老前辈的身份倚老卖老,连插入删除都如此困难,除了因为随机存取查找复杂度低以外一无是处。完全不适合作为存储表的对象,没有动态变长的东西!

接着是ArrayList

Array自以为换个马甲就好使了。ArrayList是类型不安全的,虽有线性表的优点,但是类型不安全,内部默认装的是object类型的,导致它存取时存在装箱拆箱的操作。没有泛型,狗都不用。

List<T>的优点

List<T>是谦卑的,虽然HashSet<T>拥有更高的效率,但是它是一个使用哈希表的集结构,不允许出现重复元素,因此和表在定位上还是有区别的,所以神不会和他计较。作为表结构,无论是线性表的List<T>还是链表的LinkedList<T>,神已经赢了太多太多了。而对于Dictionary<K,V>,神慈悲地包容了它,可以使用ToDictionary ()方法转换为Dictionary(using System.Linq)。而Dictionary中的键或者值也可以通过ToList()方法转换为List<T>

因此,线性表请认准List<T>,链表则使用LinkedList<T>,字典请使用Dictionary<K,V>。哈希表/数据集请使用HashSet<T>

只查找,首选List;
插入为主,查找和删除为辅,首选LinkedList;
删除为主,查找和插入为辅,首选Dictionary;

想要效率高,泛型不可少。不使用泛型的数据类还是往后稍稍吧。


一些常见,通用的委托和接口

在这里插入图片描述
在这里插入图片描述
List中提供了许多方法,通过方法名我们一眼就知道这些方法是干什么的了,你可能注意到了其中重载的一些用于接受接口和泛型委托的方法,例如public void Sort(Comparison<T> comparison);public void Sort(IComparer<T> comparer);这些。此处需要介绍两个比较常用的委托(接口):ComparisonEnumerator

Comparison

Comparison——比较器,对应的接口是IComparerIComparer接受两个同类型变量的比较,这两个变量一个叫左值x,一个叫右值y。
比较器的返回值是int,默认地,如果左值大于右值,那么比较器的返回值是>0的,而如果右值大于左值则返回值<0。左右值相等则等于0

在C#中list提供了这个方法:

public void Sort(Comparison<T> comparison);
public void Sort(int index, int count, IComparer<T> comparer);
public void Sort();
public void Sort(IComparer<T> comparer);

这是个用于list的默认排序方法,当我们直接调用排序的时候,将会自动地对内部数据进行升序排序:

List<int> list = new List<int>();
list.Add(1);
list.Add(3);
list.Add(2);
list.Add(6);
list.Add(4);
for (int i = 0; i < list.Count; i++)
{Debug.Log(list[i]);
}
// 输出:1 3 2 6 4
list.Sort();
for (int i = 0; i < list.Count; i++)
{Debug.Log(list[i]);
}
// 输出:1 2 3 4 6

那么如果我们想要实现降序排序怎么办呢?我们就可以使用这个委托来解决,刚才我们说,在委托中左值大于右值,那么比较器的返回值是>0的,而如果右值大于左值则返回值<0。左右值相等则等于0。那么如果我们改变了默认委托的返回值,使得左值大于右值时返回值为<0不就可以实现降序排序了吗:

void Start()
{List<int> list = new List<int>();list.Add(1);list.Add(3);list.Add(2);list.Add(6);list.Add(4);for (int i = 0; i < list.Count; i++){Debug.Log(list[i]);}// 输出:1 3 2 6 4list.Sort(Desc);for (int i = 0; i < list.Count; i++){Debug.Log(list[i]);}// 输出:6 4 3 2 1
}
public int Desc(int x, int y)
{if (x > y)//修改委托的返回值逻辑{return -1;}else{return 1;}
}

甚至我们还可以根据自己的需求来修改排序,例如我希望右值为3的时候返回0:

public int Desc(int x, int y)
{if (y == 3){return 0; //虽然可以自定义,但并无卵用,Sort方法使用的据说是快速排序加堆排序// 除非你真的很了解源码,不然最后结果是怎么样就不晓得了}if (x > y){return -1;}else{return 1;}
}
// 排序后输出: 3 6 4 2 1

同样我们也支持匿名函数,使用lambda表达式和三目运算符来实现匿名函数的最简化:

list.Sort((x, y) => { return x > y ? -1 : 1; });

那么既然List可以接受泛型,当然也能接受类,我们可否直接对类进行排序呢?答案是不行的:

public class Item
{int Money;public Item(int i){Money = i;}
}
void Start()
{List<Item> list = new List<Item>();Item item1 = new Item(1);Item item2 = new Item(3);Item item3 = new Item(2);Item item4 = new Item(6);Item item5 = new Item(4);list.Add(item1);list.Add(item2);list.Add(item3);list.Add(item4);list.Add(item5);for (int i = 0; i < list.Count; i++){Debug.Log(list[i].Money);}list.Sort();// 报错,不是可比较类型for (int i = 0; i < list.Count; i++){Debug.Log(list[i].Money);}
}

之所以无法进行比较,是因为我们所定义的这个Item类并没有继承IComparable接口,如果我们想要类可比较,有两种方法:第一种就是像我们刚才讲的,为委托重写写一个接受两个Item类型参数的返回值为int类型的函数,以比较它们的money属性:

void Start()
{/*省略部分重复代码 */list.Sort(Desc);
}
public int Desc(Item x, Item y)
{if (x.Money > y.Money){return -1;}else{return 1;}
}

但是这样的话有几个问题,首先因为函数是定义在类外的,如果需要我们比较的是一个私有变量那这个方法就不可行了;其次,把比较的方法暴露在外面也不符合我们封装的初衷。

另一个更好的做法是让类继承IComparable<T>接口(特别注意要继承带泛型的接口而不是接受objectIComparable,避免装箱拆箱),那么list就能自动传入比较的方法:

public class Item : IComparable<Item>
{int Money;public Item(int i){Money = i;}public int CompareTo(Item other){if (this.Money > other.Money){return 1;}else{return -1;}}public int GetMoney(){return this.Money;}
}
void Start()
{List<Item> list = new List<Item>();Item item1 = new Item(1);Item item2 = new Item(3);Item item3 = new Item(2);Item item4 = new Item(6);Item item5 = new Item(4);list.Add(item1);list.Add(item2);list.Add(item3);list.Add(item4);list.Add(item5);for (int i = 0; i < list.Count; i++){Debug.Log(list[i].GetMoney());}// 输出: 1 3 2 6 4list.Sort();for (int i = 0; i < list.Count; i++){Debug.Log(list[i].GetMoney());}// 输出: 1 2 3 4 6
}

使用上述的代码,我们就实现了很好的封装,既能保证money是一个私有的变量,又可以实现list中对item类的排序。


Enumerator

Enumerator——枚举器,当我们需要遍历某个数据结构的时候,往往需要用到枚举器。通常一些数据类继承了IEnumerator接口,我们可以用其中的GetEnumerator()方法来实例化这个枚举器:

IEnumerator enumerator = list.GetEnumerator();

使用枚举器可以遍历整个数据结构,其中枚举器提供了三个成员:MoveNext,Current,Reset

    public interface IEnumerator{object Current { get; }bool MoveNext();void Reset();}

当使用枚举器的时候,这样遍历:

public class Item : IComparable<Item>
{int Money;public Item(int i){Money = i;}public int CompareTo(Item other){if (this.Money > other.Money){return 1;}else{return -1;}}public int GetMoney(){return this.Money;}
}
void Start()
{Initiate();IEnumerator enumerator = list.GetEnumerator();while (enumerator.MoveNext()){Item newitem = (Item)enumerator.Current;Debug.Log(newitem.GetMoney());}enumerator.Reset();// 输出: 1 3 2 6 4
}

通过枚举器也可以实现遍历,问题在于枚举器的返回类型是object,这又避免不了装箱拆箱操作了。

我们也可以直接使用List内部提供的枚举器,这个枚举器是可以立即释放的,因为它继承了IDisposable接口:

public struct Enumerator : IEnumerator<T>, IEnumerator, IDisposable
{public T Current { get; }public void Dispose();public bool MoveNext();
}

只需使用using即可在使用完毕之后将其立即释放,实际上直接使用内部提供的枚举器反而更好,因为list内部的枚举值Current返回类型是对应的泛型而非object

using (var enumerator = list.GetEnumerator())
{while (enumerator.MoveNext()){// 由于返回值是泛型类型,所以可以直接调用方法而无需拆箱Debug.Log(enumerator.Current.GetMoney());}
}
enumerator.Reset(); // 编译错误,在using语句块外对象已经被销毁

你可能也注意到了,在编译器中提供了两个很相似的接口:IEnumerableIEnumerator。根据词性我们知道,前者是可枚举的意思,后者是枚举器。当一个类继承了IEnumerable<T>的接口时,实现的接口方法会提供GetEnumerator(),需要实现IEnumerable以及IEnumerable<T>的接口,并返回对应枚举器。当一个类继承了IEnumerable的时候,我们才可以使用foreach来进行遍历。

以下摘自「Unity3D」(6)协程使用IEnumerator的几种方式

除此之外,你可能也发现了,IEnumerator正是协程定义时的关键字,有意思的是协程的执行正是通过枚举器实现的,每个定义的单个协程其实正式的名称是Routine例程,不同Routine之间协同执行,就是Coroutine协程。这个Routine需要能够分步计算,才能够互相协作,不然一路执行到底,就是一般函数了。而IEnumerator接口恰恰承担了这个分步计算的任务。每次执行就是一次MoveNext(),并且可以通过Current返回执行中的结果。

所以,带有yield指令的IEnumerator的函数,最终会被编译成一个实现了IEnumerator接口的类,这是C#自带的功能。


经过多日对C#的学习,我已经被其深深地折服,java的特性,python的灵活,c++的花里胡哨,所有语言之主,唯一真神。厦门!🙏

相关文章:

【C#学习笔记】数据类中常用委托及接口——以List<T>为例

文章目录 List\<T\>/LinkedList \<T\>为什么是神&#xff1f;&#xff08;泛型为什么是神&#xff09;一些常见&#xff0c;通用的委托和接口ComparisonEnumerator List<T>/LinkedList <T>为什么是神&#xff1f;&#xff08;泛型为什么是神&#xff0…...

idea的断点调试

1、行断点 首先在代码的最左侧点击会显示红色的圆圈 第二步在main方法中右键选中debug run进行运行 会出现下面图片的情况 出现上图之后&#xff0c;点击console 下一步 这个时候就可以看到调试的结果了 6、方法调用栈&#xff1a;这里显示了该线程调试所经过的所有方法&…...

vue和react学哪一个比较有助于以后发展?

前言 首先声明vue和react这两个框架都是很优秀的前端框架&#xff0c;使用的人群下载量上数量也是相当的庞大&#xff0c;这篇文章没有贬低或者攻击任何一个框架的意思&#xff0c;只在于根据答主的问题来对这两个框架做出对比&#xff0c;以方便大家更加清晰的了解到当下vue和…...

【SkyWalking】分布式服务追踪与调用链系统

1、基本介绍 SkyWalking是一个开源的观测平台&#xff0c;官网&#xff1a;Apache SkyWalking&#xff1b; 可监控&#xff1a;分布式追踪调用链 、jvm内存变化、监控报警、查看服务器基本配置信息。 2、SkyWalking架构原理 在整个skywalking的系统中&#xff0c;有三个角色&am…...

Python“牵手”速卖通商品详情API接口运用场景及功能介绍

速卖通电商API接口是针对速卖通提供的电商服务平台&#xff0c;为开发人员提供了简单、可靠的技术来与速卖通电商平台进行数据交互&#xff0c;实现一系列开发、管理和营销等操作。其中包括商品详情API接口&#xff0c;通过这个API接口商家可以获取商品的详细信息&#xff0c;包…...

java调用python脚本的示例

java调用python脚本的示例 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class JavaCallPythonScript {public static void main(String[] args) {// 调用Python脚本的命令String pythonScriptPath "path/to/y…...

【C语言】柔性数组(可边长数组)

一、介绍 柔性数组&#xff08;Flexible Array&#xff09;&#xff0c;又称可变长数组。一般数组的长度是在编译时确定&#xff0c;而柔性数组对象的长度在运行时确定。在定义结构体时允许创建一个空数组&#xff08;例如&#xff1a;arr [ 0 ] &#xff09;&#xff0c;该数…...

C++信息学奥赛1131:基因相关性

这段代码的功能是比较两个字符串的相似度&#xff0c;并根据给定的阈值判断是否相似。 解析注释后的代码如下&#xff1a; #include <iostream> #include <string> using namespace std;int main() {double bf; // 定义双精度浮点数变量bf&#xff0c;用于存储阈…...

如何保证分布式系统中服务的高可用性:应对 ZooKeeper Leader 节点故障的注册处理策略

推荐阅读 AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI绘画、AI讲话、翻译,GPU点亮AI想象空间 资源分享 「java、python面试题」来自UC网盘app分享&#xff0c;打开手机app&#xff0c;额外获得1T空间 https://dr…...

SQL注入之延时注入

文章目录 延时注入是什么&#xff1f;延时注入获取数据库版本号 延时注入是什么&#xff1f; 延时注入就是利用sleep()函数通过if语句判断所写的语句真假&#xff0c;如果为真返回我们想要的东西&#xff08;例如&#xff1a;数据库的长度&#xff0c;数据库的名字等&#xff0…...

运维高级学习--Docker(二)

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 #拉取mysql5.6和owncloud镜像 [rootlocalhost ~]# docker pull mysql:5.6 [rootlocalhost ~]# docker pull owncloud [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED …...

QT的核心——信号与槽

目录 回顾C 语言信号 1、信号与槽 2、关联信号与槽 2.1自动关联信号与槽 2.2手动关联信号与槽 2.3断开信号与槽 3、自定义信号 3.1自定义信号使用条件 3.2自定义槽函数使用条件 4、信号与槽参数传递 4.1自定义一个带参的信号 4.2关联带参的信号与槽 4.3发送一个带…...

【业务功能篇73】web系统架构演变-单体-集群-垂直化-服务化-微服务化

1.服务架构的演 1.1 单体架构 单体架构应该是我们最先接触到的架构实现了&#xff0c;在单体架构中使用经典的三层模型&#xff0c;即表现层&#xff0c;业务逻辑层和数据访问层。 单体架构只适合在应用初期&#xff0c;且访问量比较下的情况下使用&#xff0c;优点是性价比很…...

MyCAT命令行监控

9066端口 &#xff0c;用mysql命令行连接 Mysql –utest –ptest –P9066 show help 可显示所有相关管理命令 显示后端物理库连接信息&#xff0c;包括当前连接数&#xff0c;端口 Show backend Show connection 显示当前前端客户端连接情况&#xff0c;已经网络流量信息、…...

【python】正则表达式匹配数据

前言 使用正则表达式处理数据&#xff0c;可进行字符串匹配、提取和替换等操作。在python中&#xff0c;通过re库完成正则匹配的操作。 一、正则语法规则 1.常用匹配符 模式描述^匹配字符串开头$匹配字符串结尾.匹配任意字符*匹配前面的字符零次或多次匹配前面的字符一次或多…...

【C++】用Windows API在控制台实现选择选项

2023年8月23日&#xff0c;周三上午 今天上午花了一个小时来实现这个 这个程序在碰到边界时会发出声音&#xff0c; 通过调用Windows API的Beep函数来实现。 #include<Windows.h> #include<conio.h> #include<iostream> #include<cstdlib>const int …...

Golang 批量执行/并发执行

提到Golang&#xff0c;都说Golang 天生高并发。所以分享一下我认为的Golang高并发精髓 简单的并发执行util package util import ("context""sync" )type batchRunner struct {BatchSize intctx context.Contextchannel chan func()wg sy…...

使用go语言、Python脚本搭建一个简单的chatgpt服务网站。

使用go语言、Python脚本搭建一个简单的GPT服务网站 前言 研0在暑假想提升一下自己&#xff0c;自学了go语言编程和机器学习相关学习&#xff0c;但是一味学习理论&#xff0c;终究是枯燥的&#xff0c;于是自己弄点小项目做。 在这之前&#xff0c;建议您需要掌握以下两个技…...

基于java会议室预约系统设计与实现

摘要 一个企业的发展离不开相关的规定流程。信息化到来的今天在我们的生活当中。离不开各种信息化的支持。比如钉钉会议预约、美团买菜、扫码签到等各种信息化软件。他们涉及我们生活中的方方面面给我们的生活提供了更大的便利性。大到政府、企业办公小到人们的衣食住行都离不开…...

Ubuntu18.04 交叉编译curl-7.61.0

下载 官方网址是&#xff1a;curl 安装依赖库 如果需要curl支持https协议&#xff0c;需要先交叉编译 openssl,编译流程如下&#xff1a; Ubuntu18.04 交叉编译openssl-1.1.1_我是谁&#xff1f;&#xff1f;的博客-CSDN博客 解压 # 解压&#xff1a; $tar -xzvf curl-7.61.…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

生产管理系统开发:专业软件开发公司的实践与思考

生产管理系统开发的关键点 在当前制造业智能化升级的转型背景下&#xff0c;生产管理系统开发正逐步成为企业优化生产流程的重要技术手段。不同行业、不同规模的企业在推进生产管理数字化转型过程中&#xff0c;面临的挑战存在显著差异。本文结合具体实践案例&#xff0c;分析…...