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

.NET 7/C#11中如何使用泛型INumber执行简单数学运算?

.NET 7/C# 11 基于INumber的泛型直方图实现问题及修复

问题背景与初始代码

我希望在.NET 7/C# 11中实现一个绑定到INumber<TValue>的泛型直方图类,编写了以下代码:

public class Histogram<TValue> where TValue : INumber<TValue>
{
    public List<KeyValuePair<int, int>> CalcHistogram(List<TValue> Values, TValue CorrectValue, TValue BinSize)
    {
        // ...

        foreach (TValue value in Values)
        {
            // 版本1:手动计算分箱编号
            TValue bn = (value - CorrectValue) / BinSize;
            int binNumber = bn < 0 ? (int)(bn - 0.5) : (int)(bn + (TValue)0.5);

            // 版本2:使用Math.Round计算分箱编号
            int binNumber2 = (int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero);

            // ....
        }
        return new List<KeyValuePair<int, int>>();
    }
}

编译错误列表

  • 错误CS0019:bn < 0 无法将运算符 '<' 应用于 TValue 和 int 类型的操作数。原以为IComparable支持该操作,将0转换为TValue也无法解决(见错误3)。
  • 错误CS0019:bn - 0.5 无法将运算符 '-' 应用于 TValue 和 double 类型的操作数,原以为可以直接对两个“数字”执行减法。
  • 错误CS0030:(TValue)0.5 无法将类型 double 转换为 TValue,原以为double实现了INumber就可以直接转换。
  • 错误CS0019:binNumber * BinSize 无法将运算符 '*' 应用于 int 和 TValue 类型的操作数。
  • 错误CS1503:Math.Round(...) 参数1无法从 TValue 转换为 decimal,参数2无法从 MidpointRounding 转换为 int。

核心疑问:能否像使用float或double一样,对泛型INumber<TValue>执行运算、转换、比较等操作?


解决方案

可以像操作基础数值类型一样处理INumber<TValue>,但需要使用INumber<TValue>提供的泛型静态API,而非直接使用基础类型的语法。以下是针对每个错误的修复思路及完整修正代码:

修复错误1:泛型类型与零值的比较

不能直接将TValue与int类型的0比较,需使用TValue.Zero获取泛型类型的零值,INumber<TValue>继承了IComparable<TValue>,支持直接比较:

bool isNegative = bn < TValue.Zero;

修复错误2&3:泛型类型与常量的运算

不能直接将TValue与double常量0.5运算,需通过TValue.CreateChecked(0.5)或TValue.CreateTruncating(0.5)将0.5转换为TValue类型,同时用int.CreateChecked替代强制转换避免溢出风险:

var half = TValue.CreateChecked(0.5);
int binNumber = bn < TValue.Zero ? 
    int.CreateChecked(bn - half) : 
    int.CreateChecked(bn + half);

修复错误4:int与TValue的乘法运算

需先将int类型的binNumber转换为TValue类型,再执行乘法:

TValue binOffset = TValue.CreateChecked(binNumber) * BinSize;

修复错误5:泛型数值的舍入操作

Math.Round不支持TValue类型,可使用INumber<TValue>提供的泛型舍入方法TValue.Round,或先将TValue转换为double类型再调用Math.Round:

// 方案1:推荐使用泛型舍入方法
TValue roundedBn = TValue.Round(bn, MidpointRounding.AwayFromZero);
int binNumber2 = int.CreateChecked(roundedBn);

// 方案2:转换为double后舍入(适合浮点类型的TValue)
double bnDouble = double.CreateChecked(bn);
int binNumber2 = (int)Math.Round(bnDouble, MidpointRounding.AwayFromZero);

修正后的完整代码

public class Histogram<TValue> where TValue : INumber<TValue>
{
    public List<KeyValuePair<int, int>> CalcHistogram(List<TValue> values, TValue correctValue, TValue binSize)
    {
        var histogramCounts = new Dictionary<int, int>();
        var half = TValue.CreateChecked(0.5);

        foreach (TValue value in values)
        {
            TValue binRatio = (value - correctValue) / binSize;

            // 手动舍入实现
            int binNumber = binRatio < TValue.Zero ? 
                int.CreateChecked(binRatio - half) : 
                int.CreateChecked(binRatio + half);

            // 泛型舍入方法实现
            TValue roundedRatio = TValue.Round(binRatio, MidpointRounding.AwayFromZero);
            int binNumber2 = int.CreateChecked(roundedRatio);

            // 更新直方图计数(以binNumber为例)
            if (histogramCounts.ContainsKey(binNumber))
                histogramCounts[binNumber]++;
            else
                histogramCounts[binNumber] = 1;
        }

        return histogramCounts.OrderBy(kvp => kvp.Key).ToList();
    }
}

使用INumber的关键要点

  • 用TValue.Zero、TValue.One等静态属性替代基础类型的常量值
  • 用TValue.CreateChecked/TValue.CreateTruncating实现安全的类型转换,避免强制转换的风险
  • 优先使用TValue的静态方法(如TValue.Round、TValue.Add)替代Math类的非泛型方法
  • 所有运算的操作数必须为相同的TValue类型,需先将基础类型转换为TValue再参与运算

内容的提问来源于stack exchange,提问作者Qrt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:21:28