Created
February 27, 2024 23:10
-
-
Save admir-live/63ed3a6e6b7fca101a3f5df4b053884b to your computer and use it in GitHub Desktop.
IFormattable in .NET 8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Concurrent; | |
using System.Globalization; | |
var money = new Money(1234.56m, "USD"); | |
Console.WriteLine(money.ToString("N", new CultureInfo("en-EN"))); // 1,234.56 USD | |
money = new Money(1234.56m, "EUR"); | |
Console.WriteLine(money.ToString("N", new CultureInfo("de-DE"))); // 1.234,56 EUR | |
money = new Money(1234.56m, "JPY"); | |
Console.WriteLine(money.ToString("N", new CultureInfo("ja-JP"))); // 1,234.56 JPY | |
money = new Money(1234.56m, "GBP"); | |
Console.WriteLine(money.ToString("N", new CultureInfo("en-GB"))); // 1,234.56 GBP | |
public sealed class MoneyFormatter | |
{ | |
private static ConcurrentDictionary<string, string> CurrencySymbolsCache { get; } = new(); | |
public static string Format(Money money, string? format, IFormatProvider? formatProvider) | |
{ | |
formatProvider ??= CultureInfo.CurrentCulture; | |
format = EnsureValidFormat(format); | |
return format.ToUpperInvariant() switch | |
{ | |
"N" => FormatAsNumber(money, formatProvider), | |
"C" => FormatAsCurrency(money, formatProvider), | |
_ => throw new FormatException($"The {format} format string is not supported.") | |
}; | |
} | |
private static string FormatAsNumber(Money money, IFormatProvider formatProvider) => | |
$"{money.Amount.ToString("N2", formatProvider)} {money.CurrencyCode}"; | |
private static string FormatAsCurrency(Money money, IFormatProvider formatProvider) => | |
$"{GetCurrencySymbol(money.CurrencyCode, formatProvider)}{money.Amount.ToString("N2", formatProvider)}"; | |
private static string EnsureValidFormat(string? format) => | |
string.IsNullOrWhiteSpace(format) ? "N" : format.Trim(); | |
private static string GetCurrencySymbol(string currencyCode, IFormatProvider formatProvider) => | |
CurrencySymbolsCache.GetOrAdd(currencyCode, code => LookupCurrencySymbol(currencyCode, formatProvider)); | |
private static string LookupCurrencySymbol(string currencyCode, IFormatProvider formatProvider) | |
{ | |
if (formatProvider is CultureInfo cultureInfo && !string.IsNullOrEmpty(cultureInfo.Name)) | |
{ | |
var region = new RegionInfo(cultureInfo.Name); | |
if (region.ISOCurrencySymbol == currencyCode) | |
{ | |
return region.CurrencySymbol; | |
} | |
} | |
return $"[{currencyCode}]"; | |
} | |
} | |
public record Money(decimal Amount, string CurrencyCode) : IFormattable | |
{ | |
private static MoneyFormatter Formatter { get; } = new(); | |
public string ToString(string? format, IFormatProvider? formatProvider) => | |
MoneyFormatter.Format(this, format, formatProvider); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's a breakdown of the code.
Purpose:
Key Parts:
The
Money
Record:Amount
: The decimal value (e.g., 1234.56).CurrencyCode
: The currency (e.g., "USD", "EUR", "JPY").MoneyFormatter
Class:Money
.Format
Method: The core function that formats based on a format code:CurrencySymbolsCache
) to efficiently store currency symbols.How it Works (Example):
Money
object representing $1234.56.money.ToString
to format it, specifying:"en-US"
culture for US-specific symbols and styles.