"Double" does not display as "IComparable <Double>"
I created a template that returns the index of the maximum value in an array. It works, but only if I go to a weird 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 use it:
List<IComparable<Double>> arr = new List<IComparable<Double>>(); // THIS WORKS
List<Double> arr = new List<Double>(); // THIS DOESN'T
A later list that 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 it be? "Double" is incompatible; taken from his definition:
public struct Double : IComparable, IFormattable, IConvertible, IComparable<Double>, IEquatable<Double>
source to share
I think the other answers have addressed the question of why your code as written does not work as you expected , and even why the code will not work in some cases . However, none of the answers show you a 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 introduced a generic constraint ( where T : IComparable<T>
) here. This tells the compiler that the T
implementation will happen regardless IComparable<T>
.
Also, I've put an enumerator in a statement using
that makes sure its method Dispose
is called.
Anyway, now when you call this method, it will work directly on IEnumerable<double>
and even infer the type parameter for you:
var someDoubles = new List<double> { 3, 2, 1 };
Console.WriteLine( FindMaxIndex( someDoubles ) ) // Prints "0";
Also, if FindMaxIndex
declared in a class static
, you can put a keyword this
in front of the original parameter to make it an extension method:
public static int FindMaxIndex<T>( this IEnumerable<T> source ) where T : IComparable<T>
{
// ...
}
You should now be able to call it like this:
list.FindMaxIndex()
source to share
double
is IComparable<double>
but is List<double>
not List<IComparable<double>>
.
And it cannot be allowed. Consider:
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 this supposed to do, then?
On can have variance associated with interfaces, so that, for example, List<string>
can be passed to IEnumerable<object>
, but this can only happen when the types participating in the variance are reference types.
source to share
John Hannah's answer is in the right direction. Below is the code.
double
maybe IComparable<double>
. However, a is List<double>
not List<IComparable<double>>
. Each item inside the list requires. You cannot use the entire list.
List<double> list = new List<double>() { 1,5,3};
Console.WriteLine(FindMaxIndex(list.Cast<IComparable<double>>()));
source to share