You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为任意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}";
    }
}

关键说明

  1. 泛型数值接口:利用.NET 7+引入的INumber<T>接口,它统一了所有数值类型的基础运算,无需针对int、ulong、decimal等类型写单独的判断逻辑。
  2. 反射调用泛型方法:通过反射动态创建对应数值类型的泛型方法实例,避免硬编码类型判断。
  3. 类型安全运算:使用T.CreateChecked(1024)创建对应类型的除数,用T.Divide执行除法,保证运算的类型安全性。
  4. 精度控制:转换为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:02:10