5

(非常に単純化された)コードが与えられます。

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' には 'First' の定義が含まれておらず、'List2' 型の最初の引数を受け入れる拡張メソッド 'First' が見つかりませんでした (using ディレクティブまたはアセンブリ参照がありませんか?)

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

これは単なる誤解を招くエラーのケースであり、問​​題は 2 つの多くの拡張メソッドがあり、どちらを選択すればよいかわからないことですか?

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

4

3 に答える 3

7

問題は、 と の両方を実装List2しているため、コンパイラはどちらを呼び出すかを認識できないことです。事実上、コンパイラが呼び出すために見つけることができる最良のものはないため、ジェネリックは適格なメソッドとしてカウントされません。したがって、暗黙的に変換できる単一のパラメーターで名前が付けられた非ジェネリック メソッドのみが検索され、何も見つかりません。IEnumerable<Class1>IEnumerable<Class2>Enumerable.First<Class1>Enumerable.First<Class2>TEnumerable.First<T>(l)Enumerable.First<T>Firstl

あなたははっきりと言うことができます

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

また

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

そして、あなたは大丈夫です。

于 2013-05-29T16:27:42.977 に答える
3

このクラスを検討してください:

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

そのため、コンパイラはどちらの First() メソッドを呼び出すIEnumerable<string>かを知りません。IEnumerable<object>この例は、状況の単純化されたバージョンです。

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

于 2013-05-29T16:39:10.337 に答える
-1

ジェネリック メソッドの問題点は何ですか?

クラスが fromList1および from から継承する場合、IEnumerable<Class2>実際にはList1IEnumerable<Class1>およびIEnumerable<Class2>(およびobject) の 3 つのタイプから継承されます。つまり、クラスには2つの異なる実装がFirst<>()あるため、コンパイラは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ので、以下は問題なくコンパイルされます。

優れた設計プラクティス

前のアプローチは有効ですが、同じインターフェイスを 2 回継承するクラスを持つことは、適切な設計方法ではありません。(あなたの場合、1回は を介し​​てClass1、2回目は の明示的な継承を介して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();
    }
}
于 2013-05-29T16:41:30.120 に答える