简体   繁体   English

如何在C#中模拟Java通用通配符

[英]How to simulate Java generic wildcards in C#

The following Java code compares the average of two arrays, one of Integers and one of Doubles. 以下Java代码比较两个数组的平均值,一个是整数,另一个是双数。

class Generic_Class<T extends Number>
{
    T[] nums; // array of Number or subclass

    Generic_Class(T[] o)
    {
        nums = o;
    }

    // Return type double in all cases.
    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)
//  Using Generic_Class<T> i get the error:
//  incompatible types: Generic_Class<Double> cannot be converted to Generic_Class<Integer>

//  Using wilcards I get no error
    boolean sameAvg(Generic_Class<?> ob)
    {
        if(average() == ob.average())
            return true;
        return false;
    }
}

The main method is like this: 主要方法是这样的:

public static void main(String args[])
{
    Integer inums[] = { 1, 2, 3, 4, 5 };
    Double  dnums[] = { 1.0, 2.0, 3.0, 4.0, 5.0 };

    Generic_Class<Integer> iob = new Generic_Class<Integer>(inums);
    Generic_Class<Double>  dob = new Generic_Class<Double>(dnums);

    System.out.println("iob average is " + iob.average());
    System.out.println("dob average is " + dob.average());

    if (iob.sameAvg(dob))
        System.out.println("Averages of iob and dob are the same.");
    else
        System.out.println("Averages of iob and dob differ.");
}

The result is: 结果是:

iob average is 3.0
dob average is 3.0
Averages of iob and dob are the same.

I've tried to do the same in C# but, since I have no wildcards, I can't accomplish the same task. 我曾尝试在C#中做同样的事情但是,由于我没有通配符,我无法完成同样的任务。

How can I do the same with C# ? 我怎样才能用C#做同样的事情?

Thank you. 谢谢。

As other answerers have said, there is no equivalent of Number in C#. 正如其他回答者所说,C#中没有相应的Number The best you can get is struct, IConvertible . 你能得到的最好的是struct, IConvertible However, there is another way of doing the generic wildcard. 但是,还有另一种方法可以执行通用通配符。

Just use another generic parameter: 只需使用另一个通用参数:

public class Generic_Class<T> where T : struct, IConvertible
{
    T[] nums;
    public Generic_Class(T[] o)
    {
        nums = o;
    }

    public double Average()
    {
        double sum = 0.0;
        for(int i=0; i < nums.Length; i++)
            sum += nums[i].ToDouble(null);
        return sum / nums.Length;
    }

    // this is the important bit
    public bool SameAvg<U>(Generic_Class<U> ob) where U : struct, IConvertible
    {
        if(Average() == ob.Average())
            return true;
        return false;
    }
}

Taking the average of a sequence of numbers is built-in to C#: 取一系列数字的平均值是内置于C#:

var iNums = new int[] { 1, 2, 3, 4, 5 };
var dNums = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0 };

var iAvg = iNums.Average();
var dAvg = dNums.Average();
var areEqual = iAvg == dAvg;

areEqual == true after running the above. 运行上面的命令后, areEqual == true

You can even do this with complex types using the Average overload that takes a Func<TSource, T> to return a value: 您甚至可以使用Average重载执行此操作,使用Func<TSource, T>返回值:

public class MyValue
{
    private static Random rnd = new Random();

    public int SomeInt { get; set; } = rnd.Next();
}

var myObjArray = new MyValue[] { new MyValue(), new MyValue(), new MyValue(), new MyValue() };

var myAvg = myObjArray.Average(o => o.SomeInt);

So no, wildcards are not available in C#, but using Generics you can simulate wildcards by having multiple overloads of the Func in this case. 所以不,C#中没有通配符,但是使用泛型,你可以通过在这种情况下多次重载Func来模拟通配符。

See IEnumerable Methods 请参阅IEnumerable方法

Just add simple interface with method double Average so you can do: 只需添加方法double Average简单界面,您就可以:

interface IAbleToGetAverage 
{
    double Average();
}

class GenericClass<T> : IAbleToGetAverage
    where T : struct, IConvertible
{
    private readonly T[] nums; // array of Number or subclass

    public GenericClass(T[] o)
    {
        nums = o;
    }


    private readonly IFormatProvider formatProvider = new NumberFormatInfo();

    public double Average()
    {
        var sum = 0.0;

        for(var i=0; i < nums.Length; i++)
            sum += nums[i].ToDouble(formatProvider);

        return sum / nums.Length;
    }


    public bool SameAvg(IAbleToGetAverage ob)
    {
        if(Math.Abs(Average() - ob.Average()) < double.Epsilon)
            return true;
        return false;
    }
}

What you're expressing in your Java code is boxed numeric types, which all extend the Number class, so it's trivial to enforce a constraint that the generic type is a number. 您在Java代码中表达的是盒装数字类型,它们都扩展了Number类,因此强制执行泛型类型为数字的约束是微不足道的。

Types work a little differently in C# because int rather that being primitive in the same sense as Java, is just a type alias for System.Int32 which is a struct . 类型在C#中的工作方式稍有不同,因为int与Java一样具有原始性,它只是System.Int32一个类型别名,它是一个struct

Also, there is no common base type (class, struct, interface, etc.) that is shared by ONLY numbers in C#, so its IMPOSSIBLE to create a constraint to enforce that the generic type is a number, has a numeric value, and therefore can have mathematical calculations performed on it. 此外,没有公共基类型(类,结构,接口等)由C#中的唯一数字共享,因此创建约束以强制通用类型是数字是不可能的 ,具有数值,并且因此可以对其进行数学计算。

Some suggestions here seem to be to use where T : IComparable, struct but I could argue that the constraint also applies to DateTime , Guid and TimeSpan - none of which are integral or floating point numbers. 这里的一些建议似乎是使用where T : IComparable, struct但我可以认为约束也适用于DateTimeGuidTimeSpan - 它们都不是整数或浮点数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM