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

如何在C#中模拟Java泛型通配符?附Java泛型类平均比较代码问题

解决C#中模拟Java泛型通配符的问题

先把你提供的Java代码清晰列出来,方便理解场景:

class Generic_Class<T extends Number> { 
    T[] nums; // Number或其子类的数组
    Generic_Class(T[] o) { nums = o; }
    // 所有情况下返回类型均为double
    double average() { 
        double sum = 0.0; 
        for(int i=0; i < nums.length; i++) 
            sum += nums[i].doubleValue(); 
        return sum / nums.length; 
    }
    // 尝试定义方法boolean sameAvg(Generic_Class<T> ob)时,使用Generic_Class<T>会报错:
    // 类型不兼容...
}

这个问题的核心是:Java里如果想让sameAvg方法接受任意数值类型的Generic_Class实例(比如Generic_Class<Integer>和Generic_Class<Double>互相调用),需要用通配符Generic_Class<? extends Number>,但C#没有直接的通配符语法,咱们可以用以下几种方式实现类似效果:

方案1:利用协变接口(推荐C# 8+)

C#的协变特性(通过out关键字标记泛型参数)可以实现类似Java? extends的效果。我们先定义一个协变接口,用来暴露平均值计算的方法,然后让泛型类实现这个接口:

// 定义协变接口,out表示这个泛型参数是协变的
public interface ICalculatableAverage<out T> where T : struct, INumber<T>
{
    double Average();
}

public class GenericClass<T> : ICalculatableAverage<T> where T : struct, INumber<T>
{
    private T[] _numbers;

    public GenericClass(T[] numbers)
    {
        _numbers = numbers;
    }

    public double Average()
    {
        double total = 0.0;
        foreach (var num in _numbers)
        {
            total += double.CreateChecked(num); // 安全转换为double
        }
        return total / _numbers.Length;
    }

    // 现在可以接受任何实现ICalculatableAverage的数值类型实例
    public bool SameAvg(ICalculatableAverage<INumber<INumber<T>>> other)
    {
        // 用极小值判断浮点数相等,避免精度问题
        return Math.Abs(this.Average() - other.Average()) < 1e-9;
    }

    // 更通用的版本,直接兼容所有数值类型
    public bool SameAvg<TOther>(ICalculatableAverage<TOther> other) 
        where TOther : struct, INumber<TOther>
    {
        return Math.Abs(this.Average() - other.Average()) < 1e-9;
    }
}

调用示例:

var intList = new GenericClass<int>(new[] { 1, 2, 3 });
var doubleList = new GenericClass<double>(new[] { 1.0, 2.0, 3.0 });
Console.WriteLine(intList.SameAvg(doubleList)); // 输出True

方案2:直接使用泛型方法

如果不想额外定义接口,也可以在泛型类里写一个泛型方法,让它接受任意数值类型的GenericClass实例:

public class GenericClass<T> where T : struct, INumber<T>
{
    private T[] _numbers;

    public GenericClass(T[] numbers)
    {
        _numbers = numbers;
    }

    public double Average()
    {
        double total = 0.0;
        foreach (var num in _numbers)
        {
            total += double.CreateChecked(num);
        }
        return total / _numbers.Length;
    }

    // 泛型方法,约束TOther为数值类型
    public bool SameAvg<TOther>(GenericClass<TOther> other) 
        where TOther : struct, INumber<TOther>
    {
        return Math.Abs(this.Average() - other.Average()) < 1e-9;
    }
}

这种方式更简洁,直接通过泛型参数TOther兼容所有数值类型的泛型实例,调用起来和方案1一样顺畅。

方案3:兼容旧版C#(无INumber接口)

如果你的项目用的是C# 7及更早版本,没有INumber泛型接口,可以用IConvertible来约束类型,手动转换为double:

public class GenericClass<T> where T : struct, IConvertible
{
    private T[] _numbers;

    public GenericClass(T[] numbers)
    {
        _numbers = numbers;
    }

    public double Average()
    {
        double total = 0.0;
        foreach (var num in _numbers)
        {
            total += num.ToDouble(null);
        }
        return total / _numbers.Length;
    }

    public bool SameAvg<TOther>(GenericClass<TOther> other) 
        where TOther : struct, IConvertible
    {
        return Math.Abs(this.Average() - other.Average()) < 1e-9;
    }
}

核心思路总结

Java的? extends Number通配符本质是允许“向上转型”,接受任何Number子类的泛型实例。在C#里,我们要么用协变接口实现这种向上兼容性,要么用泛型方法直接兼容不同的数值类型参数,两种方式都能解决你遇到的类型不兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:32:50