“Double”不会被视为“IComparable <double>”

时间:2017-05-15 13:40:49

标签: c# generics covariance

我创建了一个模板,它返回数组中最大值的索引。它有效,但只有当我传入一个奇怪的演员列表时。

    static public int FindMaxIndex<T>( IEnumerable<IComparable<T>> arr )
    {
        IEnumerator<IComparable<T>> it = arr.GetEnumerator();
        if (!it.MoveNext()) return -1;
        int index = 1, maxIndex = 0;
        IComparable<T> max = it.Current;
        while (it.MoveNext())
        {
            if (max.CompareTo( (T)(it.Current) ) < 0)
            {
                maxIndex = index;
                max = it.Current;
            }
            ++index;
        }
        return maxIndex;
    }

现在使用它:

    List<IComparable<Double>> arr = new List<IComparable<Double>>(); // THIS WORKS
    List<Double> arr = new List<Double>(); // THIS DOESN'T

后面的列表,我想使用的,给出了这个编译错误:

cannot convert from 'System.Collections.Generic.List<double>' to 'System.Collections.Generic.IEnumerable<System.IComparable<double>>'

这怎么可能? “双重”是一个可比较的;取自其定义:

public struct Double : IComparable, IFormattable, IConvertible, IComparable<Double>, IEquatable<Double>

4 个答案:

答案 0 :(得分:4)

我认为其他答案已经解决了为什么您的代码编写为isn't working the way you'd expect,甚至为什么code will fail in some cases。但是,没有一个答案显示你想要的方式:

public static int FindMaxIndex<T>( IEnumerable<T> source ) where T : IComparable<T>
{
    using( var e = source.GetEnumerator() )
    {
        if( !e.MoveNext() ) return -1;

        T maxValue = e.Current;
        int maxIndex = 0;

        for( int i = 1; e.MoveNext(); ++i )
        {
            if( maxValue.CompareTo( e.Current ) < 0 )
            {
                maxIndex = i;
                maxValue = e.Current;
            }
        }

        return maxIndex;
    }
}

所以我在这里引入了一个通用约束(where T : IComparable<T>)。这告诉编译器无论发生什么T,它都会实现IComparable<T>

另外,我已将枚举器放在using语句中,以保证调用其Dispose方法。

无论如何,现在当你调用这个方法时,它会直接在IEnumerable<double>上工作,甚至可以为你推断出类型参数:

var someDoubles = new List<double> { 3, 2, 1 };
Console.WriteLine( FindMaxIndex( someDoubles ) ) // Prints "0";

此外,如果在FindMaxIndex类中声明static,您可以将this关键字放在source参数前面,使其成为扩展方法:

public static int FindMaxIndex<T>( this IEnumerable<T> source ) where T : IComparable<T>
{
    // ...
}

现在你可以这样打电话了:

list.FindMaxIndex()

答案 1 :(得分:3)

通用协方差仅在泛型参数是引用类型时有效。因为您有一个值类型作为参数,所以它不能执行任何协变转换。

答案 2 :(得分:3)

doubleIComparable<double>,但List<double>不是List<IComparable<double>>

也不可能被允许。考虑:

private class ScrewItUp : IComparable<double>
{
    public int CompareTo(double value) => 0;
}

List<IComparable<double>> list = new List<double>(); // you propose that this work.
list.Add(new ScrewItUp()); // What's this supposed to do, then?

On可以在接口中涉及方差,例如List<string>可以传递给IEnumerable<object>,但这只有在方差中涉及的类型都是引用类型时才会发生。

答案 3 :(得分:-2)

Jon Hanna's answer是正确的方向。下面的代码工作。

double可能是IComparable<double>。但是List<double>不是List<IComparable<double>>。需要在列表中转换每个元素。你不能施放整个清单。

List<double> list = new List<double>() { 1,5,3};
Console.WriteLine(FindMaxIndex(list.Cast<IComparable<double>>()));