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

如何实现支持多数值类型的通用Progressing_Average类?

可行实现方案及分析

方案1:用.NET 7+的INumber<T>接口(推荐)

从.NET 7开始,官方提供了System.Numerics.INumber<T>这类数值抽象接口,直接给泛型T加上接口约束,就能合法执行加减除运算。代码示例:

using System.Numerics;

internal class Progressing_Average<T> where T : INumber<T>, IDivisionOperators<T, int, T>
{
    public Progressing_Average()
    {
        _Count = 0;
        Value = T.Zero;
    }

    public T Value { get; private set; }
    private int _Count { get; set; }

    public void AddValue(T input)
    {
        if (_Count == int.MaxValue)
        {
            throw new IndexOutOfRangeException("max amount has been reached! use preciseaverage or moving avg instead!");
        }
        _Count++;
        Value += (input - Value) / _Count;
    }
}

这里额外加IDivisionOperators<T, int, T>约束,是因为要支持T类型除以int类型的_Count——INumber<T>默认只支持同类型除法。

这个方案代码简洁,类型安全,性能几乎和非泛型版本一致——JIT会直接生成对应具体类型的代码,没有额外开销。

方案2:表达式树动态生成运算符(兼容旧.NET版本)

如果项目还在用.NET 7之前的版本,可以用表达式树在运行时生成加减除的委托,避免写重复代码:

using System.Linq.Expressions;

internal class Progressing_Average<T>
{
    private static readonly Func<T, T, T> _subtract;
    private static readonly Func<T, int, T> _divide;
    private static readonly Func<T, T, T> _addAssign;

    static Progressing_Average()
    {
        // 生成减法逻辑委托
        var paramA = Expression.Parameter(typeof(T), "a");
        var paramB = Expression.Parameter(typeof(T), "b");
        _subtract = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(paramA, paramB), paramA, paramB).Compile();

        // 生成T除以int的委托
        var paramDividend = Expression.Parameter(typeof(T), "dividend");
        var paramDivisor = Expression.Parameter(typeof(int), "divisor");
        var convertDivisor = Expression.Convert(paramDivisor, typeof(T));
        _divide = Expression.Lambda<Func<T, int, T>>(Expression.Divide(paramDividend, convertDivisor), paramDividend, paramDivisor).Compile();

        // 生成加法赋值委托
        var paramTarget = Expression.Parameter(typeof(T), "target");
        var paramValue = Expression.Parameter(typeof(T), "value");
        var addAssignExpr = Expression.AddAssign(paramTarget, paramValue);
        _addAssign = Expression.Lambda<Func<T, T, T>>(addAssignExpr, paramTarget, paramValue).Compile();
    }

    public Progressing_Average()
    {
        _Count = 0;
        Value = default!;
    }

    public T Value { get; private set; }
    private int _Count { get; set; }

    public void AddValue(T input)
    {
        if (_Count == int.MaxValue)
        {
            throw new IndexOutOfRangeException("max amount has been reached! use preciseaverage or moving avg instead!");
        }
        _Count++;
        var delta = _subtract(input, Value);
        var adjustment = _divide(delta, _Count);
        Value = _addAssign(Value, adjustment);
    }
}

这个方案第一次使用某个泛型实例(比如Progressing_Average<double>)时,会有一次表达式编译的开销,但后续调用性能和非泛型版本差距极小,大部分场景下完全可以忽略。

方案3:手动做类型分支(不推荐)

也可以写一个静态辅助类,为每个支持的数值类型(float、double、decimal)单独实现加减除方法,然后在泛型类里通过typeof(T)判断调用对应方法。但这种方式需要为每个类型写分支代码,比表达式树繁琐,维护成本也高,一般不建议。

性能对比&方案选择
  • INumber方案:性能和非泛型几乎无差,代码又简洁,是.NET 7+项目的首选。
  • 表达式树方案:初始化有微小开销,后续调用性能接近非泛型,适合旧版本.NET项目,能避免大量重复代码。
  • 单独创建类:性能最优,但会导致代码大量重复,维护麻烦。除非是极端敏感的高频调用场景,且支持的类型极少,否则不建议。如果一定要用这种方式,建议把核心计算逻辑抽成私有静态方法,让各个类型的类调用,减少重复。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:24:27