I have created a template that returns the index of the max value in an array. It works, but only if I pass in a weirdly cast List.
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;
}
Now to use it:
List<IComparable<Double>> arr = new List<IComparable<Double>>(); // THIS WORKS
List<Double> arr = new List<Double>(); // THIS DOESN'T
The later list, which is what I would like to use, gives this compiler error:
cannot convert from 'System.Collections.Generic.List<double>' to 'System.Collections.Generic.IEnumerable<System.IComparable<double>>'
How can this be? "Double" IS an IComparable; taken from its definition:
public struct Double : IComparable, IFormattable, IConvertible, IComparable<Double>, IEquatable<Double>
I think other answers have addressed why your code as written isn't working the way you'd expect and even why the code will fail in some cases. However, none of the answers show you the way to do what you want:
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;
}
}
So I've introduced a generic constraint here (the where T : IComparable<T>
). That tells the compiler that whatever T
happens to be, it will implement IComparable<T>
.
Also, I've put the enumerator in a using
statement which will guarantee that its Dispose
method is called.
Anyway, now when you call this method it will work directly on IEnumerable<double>
and even deduce the type parameter for you:
var someDoubles = new List<double> { 3, 2, 1 };
Console.WriteLine( FindMaxIndex( someDoubles ) ) // Prints "0";
Also, if FindMaxIndex
is declared in a static
class, you can put the this
keyword in front of the source parameter to make it an extension method:
public static int FindMaxIndex<T>( this IEnumerable<T> source ) where T : IComparable<T>
{
// ...
}
Now you'll be able to call it like this:
list.FindMaxIndex()
Generic covariance is only valid when the generic argument is a reference type. Because you have a value type as the argument, it cannot perform any covariant conversions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With