如何为任意INumber类型实现C#自定义Format Provider?
实现通用数值类型的自定义K格式格式化器
原代码的问题在于直接将arg强制转换为double,对于ulong、decimal等无法直接安全转换为double的数值类型会抛出InvalidCastException,同时无法通过统一逻辑处理所有实现INumber<T>(.NET 7+)的数值类型。以下是通用版本的实现,无需大量类型判断:
using System; using System.Numerics; public class NumericFormatter : IFormatProvider, ICustomFormatter { public object? GetFormat(Type? formatType) { return formatType == typeof(ICustomFormatter) ? this : null; } public string Format(string? format, object? arg, IFormatProvider? formatProvider) { formatProvider ??= this; if (!Equals(formatProvider)) return string.Empty; if (arg == null) return string.Empty; // 处理以K开头的格式 if (!string.IsNullOrEmpty(format) && format.StartsWith('K', StringComparison.OrdinalIgnoreCase)) { return HandleKFormat(arg, format); } // 非K格式时,调用数值类型自身的格式化逻辑 if (arg is IFormattable formattable) return formattable.ToString(format, formatProvider); return arg.ToString() ?? string.Empty; } private string HandleKFormat(object arg, string format) { // 通过反射调用泛型方法,适配所有INumber<T>类型 var numericType = arg.GetType(); var processMethod = typeof(NumericFormatter) .GetMethod(nameof(ProcessKFormat), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)! .MakeGenericMethod(numericType); return (string)processMethod.Invoke(null, new[] { arg, format })!; } private static string ProcessKFormat<T>(T value, string format) where T : INumber<T> { // 创建对应数值类型的1024常量 var divisor = T.CreateChecked(1024); // 执行类型安全的除法运算 var dividedValue = T.Divide(value, divisor); // 转换为decimal进行格式化,保证精度(也可根据需求改用double) if (decimal.TryParse(dividedValue.ToString(), out var decimalResult)) { return $"{decimalResult:#,0.0} {format}"; } // 兜底情况,直接输出运算结果 return $"{dividedValue} {format}"; } }
关键说明
- 泛型数值接口:利用.NET 7+引入的
INumber<T>接口,它统一了所有数值类型的基础运算,无需针对int、ulong、decimal等类型写单独的判断逻辑。 - 反射调用泛型方法:通过反射动态创建对应数值类型的泛型方法实例,避免硬编码类型判断。
- 类型安全运算:使用
T.CreateChecked(1024)创建对应类型的除数,用T.Divide执行除法,保证运算的类型安全性。 - 精度控制:转换为decimal进行格式化,避免double类型带来的精度丢失问题,适合处理大整数或高精度数值。
测试验证
var formatter = new NumericFormatter(); Debug.WriteLine(formatter.Format("K", 1024d, formatter)); // 输出:1.0 K Debug.WriteLine(formatter.Format("K", 1024, formatter)); // 输出:1.0 K Debug.WriteLine(formatter.Format("K", 2048ul, formatter));// 输出:2.0 K Debug.WriteLine(formatter.Format("K", 3072m, formatter));// 输出:3.0 K Debug.WriteLine(formatter.Format("K", 1536, formatter)); // 输出:1.5 K
扩展提示
如果需要添加其他自定义格式,只需在Format方法中增加格式判断分支,再添加对应的泛型处理方法即可,无需修改核心的类型适配逻辑。
内容的提问来源于stack exchange,提问作者totalZero
相关产品推荐
相关产品推荐

