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
    法定货币 – 例如 USDEURJPY
  • SpecialUnit – e.g., SDR, IMF units
    特殊单位 – 例如 SDR、IMF 单位
  • SpecialReserve
    特殊储备
  • PreciousMetal – e.g., XAU, XAG, XPT, XPD
    贵金属 – 例如 XAUXAGXPTXPD

仅过滤法定货币

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:

编码愉快!

Back to Blog

相关文章

阅读更多 »

掌握在 .NET 中使用 NuGet 包

NuGet到底是什么?想象一下,NuGet是 .NET 版的 Amazon 或 Mercado Libre。你不会自己制造家具的每一颗螺丝,而是向商店购买它们。- Package...