news 2026/9/25 8:46:36

Humanizer CollectionHumanizeExtensions 完全指南:把 IEnumerable 变成人类可读的自然语言列表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Humanizer CollectionHumanizeExtensions 完全指南:把 IEnumerable 变成人类可读的自然语言列表
  • 开发工具

【免费下载链接】Humanizer

Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities

项目地址:https://gitcode.com/gh_mirrors/hu/Humanizer
点击查看免费下载

Humanizer 是一套面向 .NET 的字符串、枚举、日期、时间与数字处理工具库,而CollectionHumanizeExtensions负责其中最关键的一环:把IEnumerable<T>集合"人化"成符合自然语言习惯的列表文本(如"1, 2 and 3")。本文以 CollectionHumanizeExtensions.md 收录的公开 API 为骨架,结合 CollectionHumanizeExtensions.cs 源码、CollectionHumanizeTests.cs 测试与各语言 Locale 数据,系统讲解 6 个重载的使用方式、自定义格式化函数、分隔符语义、多语言注册机制与底层实现原理。读完本文,你将能在自己的 .NET 项目中熟练使用Humanize生成带牛津逗号、符合文化习惯的集合列表,并能按需定制集合格式化器。

一、类概览与核心职责

CollectionHumanizeExtensions是一个静态扩展类,签名如下:

public static class CollectionHumanizeExtensions

它继承自System.Object,本身不保存任何状态,所有方法均为对IEnumerable<T>的扩展方法,作用是把集合"Humanizes(人化)"为一个人类可读的列表字符串。其 XML 注释给出的定位是:

Humanizes an IEnumerable into a human readable list

从 CollectionHumanizeExtensions.cs 可以看到,类的全部 8 个公开扩展方法(文档版本列出 6 个核心重载)都只是薄薄的"外观层"——它们真正的工作全部委托给Configurator.CollectionFormatter(当前线程文化对应的集合格式化器),这种"扩展方法 + 配置器委托"的架构保证了:用户只需关心语义,分隔符、连词、逗号风格等文化差异由底层 formatter 自动处理。

二、六个核心重载:签名、参数与返回值

该 API 文档共收录 6 个重载,它们在是否传入displayFormatter、是否传入separator两个维度上排列组合,覆盖了绝大多数使用场景。统一约定如下:

  • 泛型参数T:集合元素类型;
  • 参数collection:IEnumerable<T>,不能为null,否则抛出ArgumentNullException;
  • 参数displayFormatter:逐元素格式化委托,两种形态Func<T, string>或Func<T, object>,均不能为null;
  • 参数separator:元素间的分隔字符串;
  • 返回值:string,即格式化后的列表文本。
重载签名行为
①Humanize<T>(this IEnumerable<T> collection)对每个元素调用ToString(),使用当前文化的默认分隔符与连词(如英文", "+and)
②Humanize<T>(this IEnumerable<T> collection, string separator)对每个元素调用ToString(),使用调用方提供的分隔符
③Humanize<T>(this IEnumerable<T> collection, Func<T, object> displayFormatter)用委托格式化每个元素(返回值会被转成字符串),默认分隔符
④Humanize<T>(this IEnumerable<T> collection, Func<T, object> displayFormatter, string separator)委托格式化 + 自定义分隔符
⑤Humanize<T>(this IEnumerable<T> collection, Func<T, string> displayFormatter)用委托格式化每个元素(直接得到字符串),默认分隔符
⑥Humanize<T>(this IEnumerable<T> collection, Func<T, string> displayFormatter, string separator)委托格式化 + 自定义分隔符

1. 无参调用:默认文化分隔符

public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection);

这是最常用的入口。源码中它直接委托给Configurator.CollectionFormatter.Humanize(collection)(见 CollectionHumanizeExtensions.cs),底层会走o => o?.ToString()+ 默认分隔符的路径(DefaultCollectionFormatter.cs)。

典型输出(英文文化):

new[] { 1, 2, 3 }.Humanize(); // "1, 2 and 3" new[] { "Alice", "Bob", "Charlie" }.Humanize(); // "Alice, Bob and Charlie" new[] { "single" }.Humanize(); // "single" new string[] { }.Humanize(); // ""

注意空集合与单元素集合的边界行为:0 个元素返回空字符串"",1 个元素直接返回该元素本身(见 DefaultCollectionFormatter.cs)。

2. 指定分隔符

public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection, string separator);

把默认连词替换为任意字符串。底层走Humanize(collection, o => o?.ToString(), separator)(DefaultCollectionFormatter.cs):

new[] { 1, 2, 3 }.Humanize(" | "); // "1 | 2 | 3" new[] { "Alice", "Bob" }.Humanize("; "); // "Alice; Bob"

测试HumanizeUsesSeparatorWhenMoreThanOneItemIsInCollection验证了collection.Humanize("or")输出"A String or Another String"(见 CollectionHumanizeTests.cs)。

3/4. 对象形态的 displayFormatter(Func<T, object>)

public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection, System.Func<T, object> displayFormatter); public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection, System.Func<T, object> displayFormatter, string separator);

委托返回任意object,底层通过objectFormatter(item)?.ToString()转字符串后拼接(DefaultCollectionFormatter.cs)。适用于"只投影不排版"的场景:

var numbers = new[] { 1, 2, 3 }; numbers.Humanize(n => n * 2); // "2, 4 and 6" numbers.Humanize(n => n * 2, " - "); // "2 - 4 - 6"

对应测试HumanizeUsesObjectDisplayFormatter与HumanizeUsesObjectDisplayFormatterWhenSeparatorIsProvided(CollectionHumanizeTests.cs)分别验证了"1, 2, and 3"与"1, 2, or 3"的输出。

5/6. 字符串形态的 displayFormatter(Func<T, string>)

public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection, System.Func<T, string> displayFormatter); public static string Humanize<T>(this System.Collections.Generic.IEnumerable<T> collection, System.Func<T, string> displayFormatter, string separator);

委托直接返回字符串,适合"先排版再拼接"的复杂投影,比如拼接对象多个字段:

var people = new[] { new Person { Name = "Alice", Age = 30 }, new Person { Name = "Bob", Age = 25 } }; people.Humanize(p => p.Name); // "Alice and Bob" people.Humanize(p => $"{p.Name} ({p.Age})"); // "Alice (30) and Bob (25)" people.Humanize(p => p.Name, " | "); // "Alice | Bob"

对应测试HumanizeUsesStringDisplayFormatter与HumanizeUsesStringDisplayFormatterWhenSeparatorIsProvided验证了"SomeObject #1 - One, SomeObject #2 - Two, and SomeObject #3 - Three"等输出(CollectionHumanizeTests.cs)。

三、null 与空白元素的防御性语义

CollectionHumanizeExtensions的重载对委托参数统一做了ArgumentNullException.ThrowIfNull(displayFormatter)防御(CollectionHumanizeExtensions.cs)。除此之外,底层 formatter 还有两条对调用方友好的默认规则(见 ICollectionFormatter.cs 的 remarks):

  1. 忽略空/空白项:格式化结果为null、空字符串或纯空白(IsNullOrWhiteSpace)的元素会被直接跳过,不参与拼接;
  2. 自动 Trim:保留下来的元素会先Trim()再去掉首尾空白。

这两条规则在测试中有明确证据:

// 空元素被移除 Assert.Equal("A and C", StringsWithEmptyItem.Humanize(DummyFormatter)); // ["A", " ", "C"] // 元素被 Trim Assert.Equal("A, B, and C", StringsWithWhitespace.Humanize(DummyFormatter)); // ["A", " B ", "C"]

见 CollectionHumanizeTests.cs。实现位置在 DefaultCollectionFormatter.cs:AddDisplayString只接受非空白的Trim()结果。这意味着空集合、全空元素集合都会安全地返回"",集合中的null元素也不会抛异常(测试HumanizeHandlesNullItemsWithoutAnException等予以验证,见 CollectionHumanizeTests.cs)。

四、多文化支持:牛津逗号与本地化连词

集合"人化"最体现价值的地方在于文化差异。Humanize默认使用当前线程文化(Configurator.CollectionFormatter通过ResolveForCulture(null)解析,见 Configurator.cs);若需要显式指定文化,可调用Humanize(culture)形式的重载,它会走Configurator.CollectionFormatters.ResolveForCulture(culture)(CollectionHumanizeExtensions.cs),且culture为null时同样抛ArgumentNullException(测试HumanizeThrowsWhenCultureIsNull验证,见 CollectionHumanizeTests.cs)。

四种集合格式化器实现

文化注册表CollectionFormatterRegistry(CollectionFormatterRegistry.cs)为每个 Locale 解析出一个ICollectionFormatter实现。接口定义了 6 个与扩展方法一一对应的Humanize方法(ICollectionFormatter.cs),当前仓库共有 4 种实现:

实现类语义典型示例
DefaultCollectionFormatter用默认分隔符 + 连词拼接,前三项", "分隔、末项前用连词"A, B and C"
OxfordStyleCollectionFormatter牛津逗号风格:3 项及以上在倒数第二项后加逗号"A, B, and C"
CliticCollectionFormatter把末项连词当作"附缀(clitic)"与最后一项直接拼写阿拉伯语"أ، ب و ج"(و直接缀在末项前)
DelimitedCollectionFormatter纯分隔符拼接,所有可见项之间用同一分隔符中文"A、B、C"

以牛津逗号为例,OxfordStyleCollectionFormatter.cs 覆写了GetConjunctionFormatString:

protected override string GetConjunctionFormatString(int itemCount) => itemCount > 2 ? "{0}, {1} {2}" : "{0} {1} {2}";

即仅当可显示项 ≥ 3 时,在连词前补一个逗号。测试HumanizeUsesOxfordComma验证了"A String, Another String, or A Third String"(CollectionHumanizeTests.cs)。

Locale 数据如何驱动格式化器

集合格式化器的选择并非硬编码在代码里,而是来自各语言的Locales/*.yml数据。每个 Locale 文件都含list:配置节,例如:

  • 英文(en.yml)使用engine: 'oxford',即英文默认就是牛津逗号风格;
  • 法语(fr.yml)使用engine: 'conjunction'+value: 'et',连词为法语"和";
  • 简体中文(zh-Hans.yml)使用engine: 'delimited'+value: '、',即中文顿号分隔;
  • 阿拉伯语(ar.yml)使用engine: 'clitic'+value: 'و',把连词作为附缀直接缀在末项前。

这些 YAML 配置在编译期由源生成器读取:HumanizerSourceGenerator中的 GenerationHelpers.cs 根据list.engine生成对应的 formatter 构造代码(oxford→OxfordStyleCollectionFormatter、conjunction→DefaultCollectionFormatter、clitic→CliticCollectionFormatter、delimited→DelimitedCollectionFormatter)。因此,新增一种集合风格只需要在 Locale YAML 中声明,无需改动运行时代码。

五、注册表结构与扩展机制

若想为某个文化自定义集合格式化行为,理解注册表结构是关键。CollectionFormatterRegistry继承自LocaliserRegistry<ICollectionFormatter>,其默认构造逻辑是:

public CollectionFormatterRegistry() : base(_ => new DefaultCollectionFormatter("&")) => CollectionFormatterRegistryRegistrations.Register(this);

见 CollectionFormatterRegistry.cs。两点值得注意:

  1. 兜底默认值:未显式匹配任何文化的解析结果,会得到以"&"为连词的DefaultCollectionFormatter;
  2. 注册由源生成器填充:CollectionFormatterRegistryRegistrations.Register(this)是由Humanizer.SourceGenerators在编译期生成的注册代码,把每个 Locale 的 formatter 实例写入注册表——这也是为什么 YAML 数据能驱动运行时的原因。

对外,Configurator.CollectionFormatters属性暴露了LocaliserRegistry<ICollectionFormatter>(Configurator.cs),你可以仿照 Humanizer 其它注册表(如FormatterRegistry、NumberToWordsConverterRegistry)的做法,通过注册表注入自定义ICollectionFormatter实现,从而覆盖任意文化的列表风格。

六、性能与实现细节(源码视角)

从源码结构看,四种实现都对"少元素路径"做了刻意优化:

  • DefaultCollectionFormatter对 0/1 个可见元素直接返回,不做字符串格式化;2 项与多项通过JoinLeadingItems拼接前导项、最后用GetConjunctionFormatString收尾(DefaultCollectionFormatter.cs);
  • DelimitedCollectionFormatter用StringBuilder统一追加分隔符,并把首个可见项留在 builder 之外,使单元素路径零分配(DelimitedCollectionFormatter.cs);
  • CliticCollectionFormatter类似地先持有首项、再把倒数第二项并入逗号头部,末项保持独立以便连词"附缀化"(CliticCollectionFormatter.cs)。

此外CreateDisplayItems会根据集合是否实现ICollection<T>/IReadOnlyCollection<T>预分配容量,避免不必要的扩容(DefaultCollectionFormatter.cs)。

七、实战速查:常见调用模式

需求代码输出(en 文化)
简单列表new[] { "A", "B", "C" }.Humanize()A, B, and C
自定义分隔符new[] { "A", "B" }.Humanize(" or ")A or B
投影数值new[] { 1, 2, 3 }.Humanize(n => n * 2)2, 4, and 6
投影并排版people.Humanize(p => $"{p.Name} ({p.Age})")Alice (30) and Bob (25)
指定文化new[] { "A", "B", "C" }.Humanize(new CultureInfo("en-GB"))A, B and C
空集合Array.Empty<int>().Humanize()""

测试HumanizeUsesSpecifiedCulture验证了en-GB下三元素输出"A, B and C"(CollectionHumanizeTests.cs),而HumanizeUsesSpecifiedCultureForEverySupportedLocale则对所有支持语言逐语言断言两元素/三元素的输出(CollectionHumanizeTests.cs),可作为你验证多语言行为的第一手参考。

总结

CollectionHumanizeExtensions以 6 个轻量重载覆盖了集合列表"人化"的全部常用场景:默认文化、自定义分隔符、字符串/对象两种投影委托,并且通过ICollectionFormatter+ Locale YAML + 源生成器的组合,让牛津逗号、法语et、阿拉伯语附缀و、中文顿号、等文化差异在运行时自动生效。理解 CollectionHumanizeExtensions.cs 的委托结构、ICollectionFormatter.cs 的接口契约与 CollectionHumanizeTests.cs 的边界用例,即可在项目中放心使用,也能按需扩展自己的列表风格。

  • 开发工具

【免费下载链接】Humanizer

Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities

项目地址:https://gitcode.com/gh_mirrors/hu/Humanizer
点击查看免费下载
上一篇:ElasticJob任务重试策略终极指南:固定间隔与指数退避对比分析
下一篇:aimeos-laravel商品搜索排序:相关性算法优化实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 8:46:04

ax调度:面向agentic工作负载的Kubernetes CLI编排实践

1. 从“ax”这个名字说起&#xff1a;一个被低估的调度入口第一次看到“ax”这个标题&#xff0c;很多人会以为是某个命令行工具的缩写&#xff0c;或者某个内部项目的代号。但把热搜词摊开来看——agentic、orchestrator、Kubernetes、CLI、ax调度——这几个词拼在一起&#x…

作者头像 李华
网站建设 2026/9/25 8:42:39

Atlas 300V 24G上跑通YOLO:昇腾推理部署全流程解析

搞AI算法工程的朋友&#xff0c;这两年应该没少被“国产算力”“昇腾生态”“Atlas”这几个词刷屏。尤其是做边缘视频分析、工业质检、智慧园区这类项目的团队&#xff0c;经常会在选型阶段卡在同一个问题上&#xff1a;手里的YOLO模型&#xff0c;到底怎么跑到华为Atlas上&…

作者头像 李华
网站建设 2026/9/25 8:42:04

大模型广告营销实践:货拉拉文案素材生成与智能投放全解析

大模型这阵风刮到营销广告领域&#xff0c;其实是早晚的事。货拉拉的广告业务和常规电商广告不太一样&#xff0c;它同时连接货运司机和货主两端&#xff0c;营销场景既要覆盖C端用户拉新&#xff0c;又要服务B端货主促活&#xff0c;还得配合一次次大促节点做集中爆发。这种多…

作者头像 李华
网站建设 2026/9/25 8:41:32

护网行动实战指南:红蓝紫队角色与应急处置全流程

1. 护网行动到底是什么&#xff1a;一场高强度的网络安全实战演练护网行动&#xff0c;圈内人习惯直接叫“护网”&#xff0c;本质是一场由国家或大型机构组织的、针对真实业务系统的网络安全实战攻防演练。简单说&#xff0c;就是组织方请来专业的攻击队伍&#xff08;红队&am…

作者头像 李华