2017-05-15 22 views
3

配列内の最大値のインデックスを返すテンプレートを作成しました。それは動作しますが、私が奇妙なキャストリストを渡す場合のみです。"Double"は "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; 
    } 

それを使用する:

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>

答えて

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>が実装されます。

また、Disposeメソッドが呼び出されることを保証するusingステートメントに列挙子を入れました。とにかく

、今、あなたは、このメソッドを呼び出したとき、それはIEnumerable<double>を直接操作しても、あなたのための型パラメータを推測します:FindMaxIndexstaticクラスで宣言されている場合

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

また、あなたがthisを置くことができます

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

今、あなたはこのようにそれを呼び出すことができます:それは拡張メソッド作るためにソースパラメータの前にあるキーワード

list.FindMaxIndex() 
+0

@NuriTasdemirはい、私はそれを見ました。そのため、最後にオプションとして残しました。 OPのポストには、それがなければならないことを示す何もなかった。彼らはジェネリック制約について知っているようには見えないので、私は彼らが拡張方法について知らなかったとも仮定しました。 – Kyle

+0

for(int i = 1; e.MoveNext(); ++ i)ループが大好きです。きちんとした –

3

ジェネリックの共分散は、一般的な引数が参照型である場合にのみ有効です。引数として値型があるため、共変な変換は実行できません。

+0

「double」ではなく参照型を同じ方法で使用すると、問題のコードは失敗します。 – Evk

+0

@Evk 'Double'がクラスである場合、マシン上で正常にコンパイルされます。 – Servy

+0

私の悪い、私の側の問題だった。 – Evk

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? 

では、そのように、例えばインタフェースに関わる分散を有することができますList<string>IEnumerable<object>に渡すことができますが、これは分散に関係するタイプがすべての参照タイプである場合にのみ発生します。

+0

OPはIEnumerable > 'への変換を実行しています。これは異なっており、共変にすることができます。 – Servy

+0

@Servyはい、私は最後に言及するつもりだったが、最初にそれを言及したと思っていたので、あなたがコメントしたように既に追加していました。 –

-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>>())); 
+0

なぜdownvotes、あなたは少なくともいくつかのコメントを残すことができますか? –

関連する問題