2017-05-15 1 views
3

umgewandelt. Ich habe eine Vorlage erstellt, die den Index des Maximalwerts in einem Array zurückgibt. Es funktioniert, aber nur wenn ich eine komisch besetzte Liste übergebe."Double" wird nicht als "IComparable <Double>"

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

Nun, es zu benutzen:

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

Die spätere Liste, die ist, was ich verwenden möchte, gibt dieser Compiler-Fehler:

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

Wie kann das sein? "Double" ist ein IComparable; genommen aus seiner Definition:

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

Antwort

4

Ich denke, andere Antworten angesprochen haben, warum, wie Sie Ihren Code isn't working the way you'd expect geschrieben und auch, warum die code will fail in some cases. Doch keine der Antworten zeigen Ihnen den Weg zu tun, was Sie wollen:

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

Also habe ich hier eine generische Einschränkung eingeführt (die where T : IComparable<T>). Das teilt dem Compiler mit, dass T zufälligerweise IComparable<T> implementiert wird.

Auch habe ich den Enumerator in eine using Anweisung gesetzt, die garantieren wird, dass seine Dispose Methode aufgerufen wird.

Wie auch immer, jetzt, wenn Sie diese Methode aufrufen, wird es direkt auf IEnumerable<double> arbeiten und auch den Typ Parameter für Sie ableiten:

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

Auch wenn FindMaxIndex in einer static Klasse deklariert ist, können Sie die this setzen Stichwort vor dem Quellparameter ihm eine Erweiterungsmethode zu machen:

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

Jetzt werden Sie in der Lage sein, es so zu nennen:

list.FindMaxIndex() 
+0

@NuriTasdemir Ja, das habe ich gesehen, deshalb habe ich das als Option am Ende gelassen. In OPs Post gab es nichts, was darauf hinwies, dass es sein musste. Da sie anscheinend keine generischen Einschränkungen kennen, ging ich auch davon aus, dass sie über Erweiterungsmethoden nicht Bescheid wussten. – Kyle

+0

Liebe das für (int i = 1; e.MoveNext(); ++ i) Schleife. Ordentlich. –

3

Allgemeine Kovarianz ist nur gültig, wenn das generische Argument ein Referenztyp ist. Da Sie als Argument einen Wertetyp haben, können keine kovarianten Konvertierungen durchgeführt werden.

+0

Der betreffende Code schlägt fehl, wenn Sie auf die gleiche Weise den Referenztyp anstelle von 'double' verwenden. – Evk

+0

@Evk Es kompiliert gut auf meiner Maschine, wenn 'Double' eine Klasse ist. – Servy

+0

Mein schlechtes, war ein Problem auf meiner Seite. – Evk

3

double ist ein IComparable<double>, aber List<double> ist kein List<IComparable<double>>.

Noch könnte es erlaubt sein zu sein. Betrachte:

On kann Varianz in Schnittstellen beteiligt haben, so dass z.B. List<string> kann an IEnumerable<object> übergeben werden, aber das kann nur geschehen, wenn die an der Varianz beteiligten Typen alle Referenztypen sind.

+0

Das OP führt eine Konvertierung in 'IEnumerable > durch, die anders ist und kovariant sein kann. – Servy

+0

@Servy Ja, ich wollte das am Ende erwähnen, aber dann dachte ich, ich hätte es am Anfang erwähnt, also habe ich das bereits hinzugefügt, als du es kommentiert hast. –

-2

Jon Hanna's answer ist in die richtige Richtung. Der folgende Code funktioniert.

double kann ein IComparable<double> sein. Ein List<double> ist jedoch kein List<IComparable<double>>. Es ist erforderlich, jedes Element in der Liste zu platzieren. Du kannst die ganze Liste nicht übertragen.

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

Warum die Downvotes, könntest du wenigstens einen Kommentar hinterlassen? –

Verwandte Themen