2013-05-29 22 views
5

(非常に単純な)コードが与えられています。拡張メソッドがありません派生クラスの 'First'

public class Class1 
{ 
} 

public class Class2 : Class1 
{ 
} 

public class List1 : System.Collections.Generic.IEnumerable<Class1> 
{ 
    public new System.Collections.Generic.IEnumerator<Class1> GetEnumerator() 
    { 
     yield return new Class1();    
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 


public class List2 : List1 , System.Collections.Generic.IEnumerable<Class2> 
{  
    public new System.Collections.Generic.IEnumerator<Class2> GetEnumerator() 
    { 
     yield return new Class2();    
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

次にコード

var l = new List2(); 
var first = l.First(); 

はコンパイルされませんが、エラーに

を与える「LIST2は、」「ファースト」「まず」の定義なし拡張メソッドが含まれていません'List2'型の最初の引数を受け入れることができます(使用するディレクティブまたはアセンブリ参照がありませんか?)

List2がList1から派生していない場合、List2は有効な拡張メソッドを持っていることを証明するokをコンパイルします。

これは単に誤解を招くエラーの場合で、2つの拡張メソッドがあり、どちらを選択するのかわからないという問題がありますか?

もしそうなら、コンパイラがメソッドのオーバーロード解決で使用するのと同じ方法で、Class2がより具体的なバージョンであると伝えられないのはなぜですか?

+3

もちろん、「using System.Linq;」はありますか? –

+0

はい。 (そうでなければ、List2がList1から派生していないときはコンパイルされません)。 – sgmoore

答えて

7

問題は、コンパイラが呼び出すためにEnumerable.First<Class1>またはEnumerable.First<Class2>のかわからないようにList2は、両方IEnumerable<Class1>IEnumerable<Class2>を実装していることです。効果的には、コンパイラがEnumerable.First<T>(l)を呼び出すのに最適なTが存在しないため、対象となるメソッドとして汎用のEnumerable.First<T>を数えません。したがって、現在Firstという非ジェネリックメソッドのみが検索されています。これは、lが暗黙的に変換され、見つからないという単一のパラメータです。

あなたは明示的および

var first = l.First<Class1>(); 

または

var first = l.First<Class2>(); 

を言うと、あなたは大丈夫だことができます。

public class List1 : IEnumerable<string>, IEnumerable<object> 
     { 
      IEnumerator<object> IEnumerable<object>.GetEnumerator() 
      { 
       return GetEnumerator(); 
      } 

      public IEnumerator<string> GetEnumerator() 
      { 
       throw new NotImplementedException(); 
      } 

      IEnumerator IEnumerable.GetEnumerator() 
      { 
       return GetEnumerator(); 
      } 
     } 

ので、コンパイラが呼び出すIEnumerable<string>またはIEnumerable<object>のの最初の()メソッドを知らない:

+0

これら2つの呼び出しの違いは何ですか?また、どちらの場合でもfirst.GetType()が返されると思いますか? – sgmoore

2

は、このクラスを検討してください。この例は、あなたの状況を単純化したものです。

あなたはソースコードを持たないクラスに拡張メソッドを書いたとしますが、次のバージョンではそのクラスに対してまったく同じメソッドを実装します。このような状況では、古いコードは信頼できなくなるため、最適な解決策はコンパイル時のエラーです。

+0

良い説明。 – mehrandvd

-1

Genericメソッドの問題点は何ですか?

すると、あなたのクラスを継承List1からとIEnumerable<Class2>から、それは実際には3つのタイプから継承しています:List1IEnumerable<Class1>IEnumerable<Class2>(およびobject)。これは、あなたのクラスがFirst<>()に対して2つの異なる実装を持っていることを意味します。なぜなら、Tが何であるか把握できないので、コンパイラは一般的な引数を取らない「デフォルト」メソッドを生成しないからです。

First<>()が利用できないわけではありません.Tが何であるかを指定する必要があることを意味しています。

は、次のコードスニペットを考えてみましょう:

 var l2 = new List2(); 

     l2.First<Class1>(); 
     l2.First<Class2>(); 

First<Class1>()First<Class2>()の両方が利用可能であり、それ以来、コンパイラがTが何であるかを把握することはできません。

しかし、このコードスニペットで:

l2.First((Class2 c) => c.ToString() == ""); 

コンパイラはTはとても以下はうまくコンパイルしますClass2であることを把握することができます。

グッドデザイン練習前のアプローチが有効であるにもかかわらず

は、二度同じインターフェースを継承するクラスを持っていることは良いデザインの練習ではありません。 (あなたの場合は、 Class1で1回、明示的な継承では IEnumerable<Class2>)。より良い設計のために、共通の抽象クラスを実装し、このクラスのフォームを派生させるリストを作成することができます。

public abstract class CommonListBase 
{ 

} 

public class List1 : CommonListBase, IEnumerable<Class1> 
{ 
    public System.Collections.Generic.IEnumerator<Class1> GetEnumerator() 
    { 
     yield return new Class1(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 


public class List2 : CommonListBase, IEnumerable<Class2> 
{ 
    public System.Collections.Generic.IEnumerator<Class2> GetEnumerator() 
    { 
     yield return new Class2(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 
関連する問題