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

技术速递|使用 C# 集合表达式重构代码

作者:David Pine
排版:Alan Wang

本文是系列文章的第二篇,该系列文章涵盖了探索 C# 12功能的各种重构场景。在这篇文章中,我们将了解如何使用集合表达式重构代码,我们将学习集合初始化器、各种表达式用法、支持的集合目标类型和 spread 语法。该系列的进展情况如下:

  1. 使用主构造函数重构 C# 代码
  2. 使用集合表达式重构 C# 代码(本文)
  3. 通过为任何类型添加别名来重构您的 C# 代码
  4. 重构您的 C# 代码以使用默认 lambda 参数

这些功能延续了我们的旅程,使我们的代码更具可读性和可维护性,并且被认为是开发人员应该了解的“日常 C#”功能。

集合表达式

C# 12 引入了集合表达式,它为许多不同的集合类型提供简单且一致的语法。当使用集合表达式初始化集合时,编译器生成的代码在功能上与使用集合初始化项等效。该功能强调一致性,同时允许编译器优化低级的 C#。当然,每个团队都可以决定采用哪些新功能,如果您愿意,您可以尝试并引入这种新语法,因为之前所有初始化集合的方法都将继续工作。

对于集合表达式,元素出现在左括号 [ 和右括号 ] 之间的内联元素序列。继续阅读以了解有关集合表达式如何工作的更多信息。

初始化

C# 提供了许多语法来初始化不同的集合。集合表达式取代了所有这些,所以让我们先来看看初始化整数数组的不同方法,如下所示:

var numbers1 = new int[3] { 1, 2, 3 };
var numbers2 = new int[] { 1, 2, 3 };
var numbers3 = new[] { 1, 2, 3 };
int[] numbers4 = { 1, 2, 3 };

这四个版本在功能上都是等效的,并且编译器为每个版本生成相同的代码。最后一个示例类似于新的集合表达式语法。如果您眯起眼睛,将花括号 { 和 } 想象为方括号 [ 和 ],然后您就会读到新的集合表达式语法了。集合表达式不使用花括号,这是为了避免与现有语法产生歧义,特别是用 { } 来表示模式中的任何非空。

最后一个示例是唯一显式声明类型,而不是依赖 var。以下示例创建一个 List:

List<char> david = [ 'D', 'a', 'v', 'i', 'd' ];

同样,集合表达式不能与 var 关键字一起使用。您必须声明类型,因为集合表达式目前没有自然类型,以及可以转换为多种集合类型。对 var 赋值的支持仍在考虑中,但团队尚未确定自然类型应该是什么。换句话说,在编写以下代码时,C# 编译器会出错并显示 CS9176:集合表达式没有目标类型:

// Error CS9176: There is no target type for the collection expression
var collection = [1, 2, 3];

您可能会问自己,“既然有这么多不同的方法来初始化集合,为什么我要使用新的集合表达式语法?” 答案是,通过集合表达式,您可以使用相同的语法以一致的方式表达集合。这有助于提高代码的可读性和可维护性。我们将在接下来的部分中探讨更多优势。

集合表达式变化

您可以使用以下语法表示集合为空:

int[] emptyCollection = [];

​空集合表达式的初始化是代替以前使用“new”关键字的代码的绝佳选择,因为它已被编译器优化,以避免为某些集合类型分配内存。例如,当集合类型是数组 T[] 时,编译器会生成 Array.Empty(),它比 new int[] { } 效率更高。另一种快捷方式是使用集合表达式中的元素数量来设置集合大小,例如对于 Listx = [1, 2];使用 new List(2)。

集合表达式还允许您在不声明显式类型的情况下赋值给接口。编译器确定用于 IEnumerable、IReadOnlyList和 IReadOnlyCollection等类型的类型。如果实际使用的类型很重要,您需要声明它,因为如果有更高效的类型可用,情况可能会发生变化。同样,在编译器无法生成更高效的代码的情况下,例如当集合类型是 List时,编译器会生成一个新的 List(),它是等效的。

使用空集合表达式的优点有三个:

  • 它提供了初始化所有集合的一致方法,无论其目标类型如何。
  • 它允许编译器生成高效的代码。
  • 需要编写的代码更少。例如,您可以简单地编写 [],而不是编写 Array.Empty()或 Enumerable.Empty()。

关于高效生成代码的更多细节:使用 [] 语法生成已知的 IL。这允许运行时通过重用 Array.Empty(对于每个 T)的存储来优化,甚至更积极地内联代码。

空集合可以满足它们的目的,但是您可能需要一个具有一些初始值的集合。您可以使用以下语法用单个元素初始化集合:

string[] singleElementCollection =
["one value in a collection"
];

初始化单个元素集合类似于初始化包含多个单个元素的集合。您可以使用以下语法通过添加其他文字值来初始化包含多个元素的集合:

int[] multipleElementCollection = [1, 2, 3 /* any number of elements */];

一些历史
该功能的早期提案包括短语“集合文字”,您可能听说过与此功能相关的术语。这似乎是显而易见且合乎逻辑的,特别是考虑到前面的几个例子。 所有元素均表示为文字值。 但您不局限于使用文字。事实上,只要类型一致,您就可以轻松地使用变量初始化集合(当它们不对应时,可以使用隐式转换)。

让我们看另一个代码示例,但它使用 spread 元素来包含另一个集合的元素,使用以下语法:

int[] oneTwoThree = [1, 2, 3];
int[] fourFiveSix = [4, 5, 6];
int[] all = [.. fourFiveSix, 100, .. oneTwoThree];
Console.WriteLine(string.Join(", ", all));
Console.WriteLine($"Length: {all.Length}");
// Outputs:
//   4, 5, 6, 100, 1, 2, 3
//   Length: 7

Spread 元素是一个强大的功能,它允许您将另一个集合的元素包含在当前集合中。spread 元素是一种以简洁的方式组合集合的好方法。Spread 元素中的表达式必须是可枚举的(可查询的)。有关更多信息,请参阅 Spread 部分。

支持的集合类型

集合表达式可以与许多目标类型一起使用。该功能可识别代表集合类型的“形状”。因此,您熟悉的大多数集合都是开箱即用的。对于与该“形状”不匹配的类型(主要是只读集合),您可以应用一些属性来描述构建器模式。BCL 中需要属性/构建器模式方法的集合类型已经更新。

  • 您不太可能需要考虑如何选择目标类型,但如果您对规则感到好奇,请参阅 C# 语言参考:集合表达式 - 转换。

  • 集合表达式尚不支持字典。您可以找到扩展功能的提案:C# 功能提案:字典表达式。

重构场景

集合表达式在许多场景中都很有用,例如:

  • 初始化声明非空集合类型的空集合:
    • 字段
    • 属性
    • 局部变量
    • 方法参数
    • 返回值
    • 合并表达式作为最终的解决方案,以安全地避免异常
  • 将参数传递给需要集合类型参数的方法

让我们利用本节来探索一些示例使用场景,并考虑潜在的重构机会。当您定义包含非空集合类型的字段和/或属性的类或结构时,可以使用集合表达式来初始化它们。例如,请考虑以下 ResultRegistry 对象示例:

namespace Collection.Expressions;
public sealed class ResultRegistry
{private readonly HashSet<Result> _results = new HashSet<Result>();public Guid RegisterResult(Result result){_ = _results.Add(result);return result.Id;}public void RemoveFromRegistry(Guid id){_ = _results.RemoveWhere(x => x.Id == id);}
}
public record class Result(bool IsSuccess,string? ErrorMessage
)
{public Guid Id { get; } = Guid.NewGuid();
}

在前面的代码中,结果注册表类包含一个私有 _results 字段,该字段使用新的 HashSet()构造函数表达式进行初始化。在您选择的 IDE(支持这些重构功能)中,右键单击 new 关键字,选择 Quick Actions and Refactorings…(或按Ctrl + .),然后选择“Collection initialization can be simplified”,如下视频所示:

refactor-simplify-collection

代码已更新为使用集合表达式语法,如以下代码所示:

private readonly HashSet<Result> _results = [];

前面的代码使用 new HashSet()构造函数表达式实例化了 HashSet。 然而,在这种情况下 [] 是等效的。

Spread

许多流行的编程语言(例如 Python 和 JavaScript/TypeScript 等)都提供了 spread 语法的变体,这是一种简洁的处理集合的方式。在 C# 中,spread 元素是用于将各种集合串联成单个集合的语法。

正确的术语
Spread 元素经常与术语“spread运算符”混淆。在 C# 中,不存在“spread运算符”这样的东西。… 表达式不是运算符,它是 spread 元素语法一部分的表达式。根据定义,此语法与运算符的语法不一致,因为它不对操作数执行操作。例如,… 表达式已经存在于范围切片模式中,并且也可以在列表模式中找到。

那么 spread 元素到底是什么?它从正在“spread”的集合中获取各个值,并将它们放置在目标集合中的相应位置。Spread 元素功能还带来了重构机会。如果您有调用 .ToList 或 .ToArray 的代码,或者您想要使用即时求值,您的 IDE 可能会建议改用 spread 元素语法。例如,以下代码:

namespace Collection.Expressions;
public static class StringExtensions
{public static List<Query> QueryStringToList(this string queryString){List<Query> queryList = (from queryPart in queryString.Split('&')let keyValue = queryPart.Split('=')where keyValue.Length is 2select new Query(keyValue[0], keyValue[1])).ToList()
;return queryList;}
}
public record class Query(string Name, string Value);

可以重构前面的代码以使用 spread 元素语法,请考虑以下代码,该代码删除了 .ToList 方法调用,并使用表达式主体方法作为额外的重构版本:

public static class StringExtensions
{public static List<Query> QueryStringToList(this string queryString) =>[.. from queryPart in queryString.Split('&')let keyValue = queryPart.Split('=')where keyValue.Length is 2select new Query(keyValue[0
], keyValue[
1
])];
}

Span 和 ReadOnlySpan 支持

集合表达式支持 Span和 ReadOnlySpan类型,用于表示任意内存的连续区域。即使您不在代码中直接使用它们,您也可以从它们提供的性能改进中受益。集合表达式允许运行时提供优化,特别是当集合表达式用作参数时可以选择使用 span 的重载。

如果您的应用程序使用 span,您也可以直接赋值给 span:

Span<int> numbers = [1, 2, 3, 4, 5];
ReadOnlySpan<char> name = ['D', 'a', 'v', 'i', 'd'];

如果您使用 stackalloc 关键字,甚至还提供了使用集合表达式的重构。例如,以下代码:

namespace Collection.Expressions;
internal class Spans
{public void Example(){ReadOnlySpan<byte> span = stackalloc byte[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};UseBuffer(span);}private static void UseBuffer(ReadOnlySpan<byte> span){// TODO://   Use the span...throw new NotImplementedException();}
}

如果右键 stackalloc 关键字,选择 Quick Actions and Refactorings…(或者按 Ctrl + .),选择 Collection initialization can be simplified,如下视频所示:

refactor-collection-ex

代码已更新为使用集合表达式语法,如以下代码所示:

namespace Collection.Expressions;
internal class Spans
{public void Example(){ReadOnlySpan<byte> span =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];UseBuffer(span);}// Omitted for brevity...
}

有关详细信息,请参阅 Memory 和 Span 使用指南。

语义考虑

当使用集合表达式初始化集合时,编译器生成的代码在功能上与使用集合初始化项等效。有时,生成的代码比使用集合初始化项更有效。如以下示例:

List<int> someList = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

集合初始化项的规则要求编译器为初始化项中的每个元素调用 Add 方法。但是,如果您要使用集合表达式语法:

List<int> someList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

编译器生成的代码改为使用 AddRange,这可能更快或更优化。 编译器能够进行这些优化,因为它知道集合表达式的目标类型。

后续步骤

请务必在您自己的代码中尝试一下!敬请期待本系列的下一篇文章,我们将探讨如何通过为任何类型添加别名来重构 C# 代码。同时,您可以在以下资源中了解有关集合表达式的更多信息:

  • C# 功能提案:集合表达式

  • C# 语言参考:集合表达式

  • C# 文档:集合

相关文章:

技术速递|使用 C# 集合表达式重构代码

作者&#xff1a;David Pine 排版&#xff1a;Alan Wang 本文是系列文章的第二篇&#xff0c;该系列文章涵盖了探索 C# 12功能的各种重构场景。在这篇文章中&#xff0c;我们将了解如何使用集合表达式重构代码&#xff0c;我们将学习集合初始化器、各种表达式用法、支持的集合目…...

我的世界开服保姆级教程

前言 Minecraft开服教程 如果你要和朋友联机时&#xff0c;可以选择的方法有这样几种&#xff1a; 局域网联机&#xff1a;优点&#xff1a;简单方便&#xff0c;在MC客户端里自带。缺点&#xff1a;必须在同一局域网内。 有些工具会带有联机功能&#xff1a;优点&#xff1a;一…...

[转载]同一台电脑同时使用GitHub和GitLab

原文地址&#xff1a;https://developer.aliyun.com/article/893801 简介&#xff1a; 工作中我们有时可能会在同一台电脑上使用多个git账号&#xff0c;例如&#xff1a;公司的gitLab账号&#xff0c;个人的gitHub账号。怎样才能在使用gitlab与github时&#xff0c;切换成对应…...

【网络协议】【OSI】一次HTTP请求OSI工作过程详细解析

目录 1. 一次HTTP请求OSI工作过程 1.1 应用层(第7层) 1.2 表示层(第6层) 1.3 会话层(第5层) 1.4 传输层(第4层)...

springboot vue 开源 会员收银系统 (2) 搭建基础框架

前言 完整版演示 前面我们对会员系统https://blog.csdn.net/qq_35238367/article/details/126174288进行了分析 确定了技术选型 和基本的模块 下面我们将从 springboot脚手架开发一套收银系统 使用脚手架的好处 不用编写基础的rabc权限系统将工作量回归业务本身生成代码 便于…...

Java进阶学习笔记26——包装类

包装类&#xff1a; 包装类就是把基本类型的数据包装成对象。 看下API文档&#xff1a; deprecated&#xff1a;极力反对、不赞成的意思。 marked for removal&#xff1a;标识为去除的意思。 自动装箱&#xff1a;基本数据类型可以自动转换成包装类。 自动拆箱&#xff1a;…...

【JavaEE进阶】——要想代码不写死,必须得有spring配置(properties和yml配置文件)

目录 本章目标&#xff1a; &#x1f6a9;配置文件 &#x1f6a9;SpringBoot配置文件 &#x1f388;配置⽂件的格式 &#x1f388; properties 配置⽂件说明 &#x1f4dd;properties语法格式 &#x1f4dd;读取配置文件 &#x1f4dd;properties 缺点分析 &#x1f3…...

第十四 Elasticsearch介绍和安装

docker-compose安装 kibana: image: docker.elastic.co/kibana/kibana:7.5.1 container_name: kibana ports: - "5601:5601" environment: ELASTICSEARCH_HOSTS: http://elasticsearch:9200 depends_on: - elasticsearch…...

YOLOv10介绍与推理--图片和视频演示(附源码)

导 读 本文主要对YOLOv10做简单介绍并给出推理图片和视频的步骤演示。 YOLOv10简介 YOLOv10是清华大学的研究人员在Ultralytics Python包的基础上&#xff0c;引入了一种新的实时目标检测方法&#xff0c;解决了YOLO 以前版本在后处理和模型架构方面的不足。通过消除非最大抑…...

Java实验08

实验一 demo.java package q8.demo02;public class demo{public static void main(String[] args) {WindowMenu win new WindowMenu("Hello World",20,30,600,290);} }WindowMenu.java package q8.demo02; import javax.swing.*;public class WindowMenu extends…...

MyBatis复习笔记

3.Mybatis复习 3.1 xml配置 properties&#xff1a;加载配置文件 settings&#xff1a;设置驼峰映射 <settings><setting name"mapUnderscoreToCamelCase" value"true"/> </settings>typeAliases&#xff1a;类型别名设置 #这样在映射…...

HTML的基石:区块标签与小语义标签的深度解析

&#x1f4da; HTML的基石&#xff1a;区块标签与小语义标签的深度解析 &#x1f310; 区块标签&#xff1a;构建网页的框架&#x1f3e0; <div>&#xff1a;万能的容器&#x1f4da; <section>、<article>、<aside>&#xff1a;语义化的布局 &#x1…...

Windows域控简介

一、Windows 域控概念 Windows 域控即 Active Directory&#xff08;AD&#xff09;域控制器&#xff0c;它是 Windows Server 中的一个角色&#xff0c;用于管理网络中的用户帐户、计算机和其他设备。AD 域控制器的功能包括&#xff1a; 用户认证&#xff1a;允许用户通过用…...

项目延期,不要随意加派人手

遇到软件项目出现延期的情况时&#xff0c;不建议随意加派人手。原因如下&#xff1a; 有些任务是不可拆分的&#xff0c;不能拆分为多个并行任务&#xff0c;增加人员不会加快项目进度。新增加人员需要原有人员介绍项目中的技术架构、业务知识&#xff0c;在开发过程中也难免…...

帝国CMS验证码不显示怎么回事呢?

帝国CMS验证码有时候会不显示或打叉&#xff0c;总结自己的解决方法。 1、检查服务器是否开启GD库 测试GD库是否开启的方法&#xff1a;浏览器访问&#xff1a;/e/showkey/index.php&#xff0c;如果出现一堆乱码或报错&#xff0c;证明GD库没有开启&#xff0c;开启即可。 2…...

【必会面试题】Redis 中的 zset数据结构

目录 Redis 中的 zset&#xff08;sorted set&#xff0c;有序集合&#xff09;数据结构在底层可以使用两种不同的实现&#xff1a;压缩列表&#xff08;ziplist&#xff09; 和 跳跃表&#xff08;skiplist&#xff09;。具体使用哪种结构取决于存储元素的数量和大小&#xff…...

括号匹配数据结构

括号匹配是一种数据结构问题&#xff0c;用于检查给定的字符串中的括号是否匹配。例如&#xff0c;对于字符串 "((())())"&#xff0c;括号是匹配的&#xff0c;而对于字符串 "())("&#xff0c;括号是不匹配的。 常见的解决括号匹配问题的数据结构是栈。…...

c语言:strcmp

strcmp函数是用于比较两个字符串的库函数&#xff0c;其功能是根据ASCII值逐一对两个字符串进行比较。 语法&#xff1a;strcmp(str1, str2) 返回值&#xff1a; 如果str1等于str2&#xff0c;则返回0。 如果str1小于str2&#xff0c;则返回负数&#xff08;具体值取决于C…...

传统关系型数据库与hive的区别

数据库和Hive之间存在本质的区别&#xff0c;主要体现在设计目的、数据处理方式、数据存储、查询延迟、数据更新能力、以及适用场景等方面。下面详细阐述它们之间的主要差异&#xff1a; 设计目的与应用场景&#xff1a; 数据库&#xff1a;主要是面向事务处理&#xff08;OLTP…...

windows-386、windows-amd64、windows-arm64这三者有什么区别?

选择文件的版本出现下面问题&#xff1a; Architectures windows-386 &#xff1a;这些是针对 32 位 Windows 系统编译的。windows-amd64 &#xff1a;这些是针对具有 AMD 或 Intel x86-64 架构的 64 位 Windows 系统编译的。windows-arm64 &#xff1a;这些是针对具有 ARM 架…...

用ESP32和SSD1680驱动墨水屏,手把手教你做个低功耗电子价签原型

用ESP32和SSD1680打造低功耗电子价签&#xff1a;从硬件选型到云端更新全解析 在零售场景中&#xff0c;电子价签正逐步取代传统纸质标签&#xff0c;成为数字化门店的标配。而基于ESP32和SSD1680驱动墨水屏的方案&#xff0c;凭借其超低功耗、无线更新和低成本优势&#xff0c…...

Open-AutoGLM实战:自动刷抖音关注博主,效果惊艳,小白也能轻松上手

Open-AutoGLM实战&#xff1a;自动刷抖音关注博主&#xff0c;效果惊艳&#xff0c;小白也能轻松上手 你是不是也遇到过这种情况&#xff1a; 刷抖音时看到一个特别有趣的博主&#xff0c;想点个关注&#xff0c;结果手指一滑&#xff0c;视频过去了&#xff0c;再想找回来得翻…...

Qwen3-VL-8B支持多场景扩展:轻松接入RAG、插件系统与企业身份认证

Qwen3-VL-8B支持多场景扩展&#xff1a;轻松接入RAG、插件系统与企业身份认证 1. 项目概述 Qwen3-VL-8B AI聊天系统是一个基于通义千问大语言模型的完整Web应用解决方案。这个系统不仅仅是一个简单的聊天界面&#xff0c;而是一个具备高度扩展性的企业级AI对话平台。 系统采…...

Linux终端美化必备:cmatrix屏保软件从安装到高级玩法详解

Linux终端美化必备&#xff1a;cmatrix屏保软件从安装到高级玩法详解 每次打开终端&#xff0c;面对单调的黑白界面是否感到乏味&#xff1f;cmatrix这款经典的开源工具能让你的Linux终端瞬间变身《黑客帝国》风格的代码雨屏保。作为终端美化领域的常青树&#xff0c;它不仅安装…...

2024丨时间序列预测(Time Series Prediction)前沿技术解析与论文精要

1. 2024年时间序列预测技术全景图 时间序列预测就像给数据装上"时光望远镜"&#xff0c;让我们能够窥见未来的趋势和变化。从股票价格到天气变化&#xff0c;从设备故障预警到疫情传播预测&#xff0c;这项技术正在深刻改变各行各业的决策方式。2024年&#xff0c;这…...

手把手教你复现ownCloud高危漏洞CVE-2023-49103:从环境搭建到信息泄露验证

从零构建ownCloud漏洞靶场&#xff1a;CVE-2023-49103深度复现指南 当开源云存储系统ownCloud的graphapi组件暴露出PHP环境信息时&#xff0c;意味着什么&#xff1f;想象一下&#xff0c;攻击者通过一个未公开的URL路径&#xff0c;就能获取数据库密码、邮件服务器凭证甚至加密…...

零代码自动化:Gemma-3-12b-it镜像+OpenClaw图形化配置指南

零代码自动化&#xff1a;Gemma-3-12b-it镜像OpenClaw图形化配置指南 1. 为什么选择图形化配置 当我第一次接触自动化工具时&#xff0c;面对密密麻麻的API文档和YAML配置文件&#xff0c;那种"从入门到放弃"的感觉至今记忆犹新。直到发现OpenClaw的图形化配置界面…...

双模型协作实战:OpenClaw路由Kimi-VL-A3B-Thinking与Whisper处理音图文混合输入

双模型协作实战&#xff1a;OpenClaw路由Kimi-VL-A3B-Thinking与Whisper处理音图文混合输入 1. 需求场景与技术选型 上周我需要整理一场技术研讨会的录音和幻灯片。现场拍摄的照片包含PPT内容&#xff0c;同时手机录音记录了讲解语音——这种音图文混合素材的传统处理方式需要…...

macOS极简安装OpenClaw:gemma-3-12b-it镜像10分钟体验

macOS极简安装OpenClaw&#xff1a;gemma-3-12b-it镜像10分钟体验 1. 为什么选择OpenClawGemma组合 上周我在测试自动化工作流时&#xff0c;偶然发现OpenClaw这个开源框架。它最吸引我的是能直接在本地电脑上实现"AI操控电脑"——就像有个数字员工帮你点击鼠标、整…...

Naivechain性能基准测试终极指南:评估区块链吞吐量的完整教程

Naivechain性能基准测试终极指南&#xff1a;评估区块链吞吐量的完整教程 【免费下载链接】naivechain A blockchain implementation in 200 lines of code 项目地址: https://gitcode.com/gh_mirrors/na/naivechain 想要了解区块链的真实性能表现吗&#xff1f;Naivech…...