"Double" 没有被转换为 "IComparable<Double>"

"Double" doesn't get cast as "IComparable<Double>"

我创建了一个模板,returns 数组中最大值的索引。它有效,但前提是我传入一个奇怪的列表。

    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>>'

怎么会这样? "Double" 是一个 IComparable;取自它的定义:

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

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

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> 但这只有在涉及方差的类型都是引用类型时才会发生。

方向正确。下面的代码有效。

double 可能是 IComparable<double>。但是 List<double> 不是 List<IComparable<double>>。需要将每个元素投射到列表中。您不能投射整个列表。

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

我认为其他答案已经解决了您编写代码的原因 and even why the 。但是,none 的答案会告诉您如何做您想做的事情:

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";

另外,如果在staticclass中声明了FindMaxIndex,可以在源参数前加上this关键字,使其成为扩展方法:

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

现在您可以这样称呼它了:

list.FindMaxIndex()