ISO 4217 货币参考(适用于 .NET)— 强类型且生产就绪
发布: (2025年12月12日 GMT+8 18:41)
3 min read
原文: Dev.to
Source: Dev.to
概述
在 .NET 中处理货币看似很简单——但往往会变得一团糟。基于字符串的代码、大小写不统一、已撤销的货币、特殊单位以及各种边缘情况都可能悄无声息地破坏你的逻辑。常见的陷阱包括:
if (currencyCode == "usd" || currencyCode == "Usd" || currencyCode == "USD")
{
// ...
}
或“改进”版:
if (string.Equals(currencyCode, "usd", StringComparison.OrdinalIgnoreCase))
{
// ...
}
更糟糕的情况:
if (currency == "XXX") { /* ??? */ }
if (currency == "BYR") { /* replaced by BYN */ }
所有这些问题的根源在于货币被当作普通字符串处理。ISO 4217 是一个标准,我们应该能够以强类型的方式使用它。
思路
在编译时使用 Source Generator 生成 ISO 货币类型。生成的库为 HawkN.Currency.Reference.Iso4217。
- NuGet:
- GitHub:
一个轻量级、零依赖、源码生成、可直接用于生产环境的 .NET ISO 4217 参考库。
安装
CLI
dotnet add package HawkN.Currency.Reference.Iso4217 --version 8.0.1
包管理器
Install-Package HawkN.Currency.Reference.Iso4217 -Version 8.0.1
注册
services.AddCurrencyService();
基本用法
控制台应用
using var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddCurrencyService();
})
.Build();
var currencyService = host.Services.GetRequiredService<ICurrencyService>();
最小 API 注入
app.MapGet("/currencies", ([FromServices] ICurrencyService svc) => svc.Query().Build());
查询货币
包含特定类型
var currencies = currencyService.Query()
.Includes
.Type(CurrencyType.Fiat)
.Type(CurrencyType.SpecialUnit)
.Type(CurrencyType.SpecialReserve)
.Type(CurrencyType.PreciousMetal)
.Build();
货币类型
- Fiat – e.g.,
USD,EUR,JPY
法定货币 – 例如USD、EUR、JPY - SpecialUnit – e.g.,
SDR, IMF units
特殊单位 – 例如SDR、IMF 单位 - SpecialReserve
特殊储备 - PreciousMetal – e.g.,
XAU,XAG,XPT,XPD
贵金属 – 例如XAU、XAG、XPT、XPD
仅过滤法定货币
var fiat = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Build();
排除特定代码
var filtered = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Without(w => w.Codes(nameof(CurrencyCode.EUR), nameof(CurrencyCode.USD)))
.Build();
条件选择
var selected = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Where(x => x.Code is "EUR" or "USD")
.Build();
验证
var ok = currencyService.TryValidate("AFN", out var result);
或使用强类型枚举:
var ok = currencyService.TryValidate(CurrencyCode.AFN, out var result);
检索
按字符串
var afn = currencyService.Get("AFN");
按枚举
var afn = currencyService.Get(CurrencyCode.AFN);
历史货币
var historical = currencyService.GetAllHistorical();
foreach (var c in historical)
{
Console.WriteLine($"{c.Code} - {c.Name} (Withdrawn: {c.WithdrawnOn})");
}
好处
- 编译时安全,避免使用原始字符串文字。
- 符合 ISO 标准的数据,无需手动维护。
- 极快的查找速度。
- 极佳的开发者体验(随处可用 IntelliSense)。
- 支持 AOT 编译且无需反射。
- 静态、确定性的数据。
- 通过 CI 工作流自动更新。
项目背景
该项目最初是一个小实验:“能否使用 Source Generator 生成所有 ISO 4217 货币?”
- GitHub:
- NuGet:
编码愉快!