成都武侯区网站建设,wordpress菜单分类目录,李可做的网站,mooc网站开发流程图作者#xff1a;David Pine 排版#xff1a;Alan Wang 本文是系列文章的第二篇#xff0c;该系列文章涵盖了探索 C# 12功能的各种重构场景。在这篇文章中#xff0c;我们将了解如何使用集合表达式重构代码#xff0c;我们将学习集合初始化器、各种表达式用法、支持的集合目… 作者David Pine 排版Alan Wang 本文是系列文章的第二篇该系列文章涵盖了探索 C# 12功能的各种重构场景。在这篇文章中我们将了解如何使用集合表达式重构代码我们将学习集合初始化器、各种表达式用法、支持的集合目标类型和 spread 语法。该系列的进展情况如下
使用主构造函数重构 C# 代码使用集合表达式重构 C# 代码本文通过为任何类型添加别名来重构您的 C# 代码重构您的 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
Listchar 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: 7Spread 元素是一个强大的功能它允许您将另一个集合的元素包含在当前集合中。spread 元素是一种以简洁的方式组合集合的好方法。Spread 元素中的表达式必须是可枚举的可查询的。有关更多信息请参阅 Spread 部分。
支持的集合类型
集合表达式可以与许多目标类型一起使用。该功能可识别代表集合类型的“形状”。因此您熟悉的大多数集合都是开箱即用的。对于与该“形状”不匹配的类型主要是只读集合您可以应用一些属性来描述构建器模式。BCL 中需要属性/构建器模式方法的集合类型已经更新。 您不太可能需要考虑如何选择目标类型但如果您对规则感到好奇请参阅 C# 语言参考集合表达式 - 转换。 集合表达式尚不支持字典。您可以找到扩展功能的提案C# 功能提案字典表达式。
重构场景
集合表达式在许多场景中都很有用例如
初始化声明非空集合类型的空集合 字段属性局部变量方法参数返回值合并表达式作为最终的解决方案以安全地避免异常 将参数传递给需要集合类型参数的方法
让我们利用本节来探索一些示例使用场景并考虑潜在的重构机会。当您定义包含非空集合类型的字段和/或属性的类或结构时可以使用集合表达式来初始化它们。例如请考虑以下 ResultRegistry 对象示例
namespace Collection.Expressions;
public sealed class ResultRegistry
{private readonly HashSetResult _results new HashSetResult();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 HashSetResult _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 ListQuery QueryStringToList(this string queryString){ListQuery 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 ListQuery 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
Spanint numbers [1, 2, 3, 4, 5];
ReadOnlySpanchar name [D, a, v, i, d];如果您使用 stackalloc 关键字甚至还提供了使用集合表达式的重构。例如以下代码
namespace Collection.Expressions;
internal class Spans
{public void Example(){ReadOnlySpanbyte span stackalloc byte[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};UseBuffer(span);}private static void UseBuffer(ReadOnlySpanbyte 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(){ReadOnlySpanbyte span [1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];UseBuffer(span);}// Omitted for brevity...
}有关详细信息请参阅 Memory 和 Span 使用指南。
语义考虑
当使用集合表达式初始化集合时编译器生成的代码在功能上与使用集合初始化项等效。有时生成的代码比使用集合初始化项更有效。如以下示例
Listint someList new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };集合初始化项的规则要求编译器为初始化项中的每个元素调用 Add 方法。但是如果您要使用集合表达式语法
Listint someList [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];编译器生成的代码改为使用 AddRange这可能更快或更优化。 编译器能够进行这些优化因为它知道集合表达式的目标类型。
后续步骤
请务必在您自己的代码中尝试一下敬请期待本系列的下一篇文章我们将探讨如何通过为任何类型添加别名来重构 C# 代码。同时您可以在以下资源中了解有关集合表达式的更多信息 C# 功能提案集合表达式 C# 语言参考集合表达式 C# 文档集合