2015-09-23 6 views
5

私は2つの拡張メソッドを書いています。 1つは単一のオブジェクトで作業し、もう1つはオブジェクトの集合で作業します。拡張メソッドを呼び出すと、C#コンパイラはどちらを使用するのか混乱し、コンパイルに失敗します。C#拡張メソッドのコンパイル/名前空間の順序に基づく互換性チェックが失敗する

さらに驚くべきことに、拡張メソッドを異なる名前空間に移動すると、両方の名前空間をコールサイトに含めるとしても、名前空間がアルファベット順に特定の順序である場合にのみコンパイルが失敗します。ここで

はコードです:

public static class DBObjectExtensions 
{ 
    public static void PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
    { 
     if (obj == null) 
     { 
      return; 
     } 

     obj.Transaction.PopulateRelations<T>(new[]{ obj }, relationsToPrefetch); 
    } 

    public static void PopulateRelations<T>(this IEnumerable<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
    { 
     var first = objects.FirstOrDefault(); 
     if (first == null) 
     { 
      return; 
     } 

     first.Transaction.PopulateRelations<T>(objects, relationsToPrefetch); 
    } 
} 

これは、コンパイルを失敗した呼び出し場所ラインです:

型「にSystem.Collections:

List<ITable> list = ... // ITable inherits from IDBObject 
list.PopulateRelations(xxx); 

はエラーCS0311で失敗します。ジェネリック型またはメソッド 'Granta.MI.DBObjectExtensions.PopulateRelations(T、params Granta.MI。)の型パラメータ' T 'として使用することはできません。 RelationToPrefetch []) 'を呼び出します。 'System.Collections.Generic.List'から 'Granta.MI.IDBObject'への暗黙的な参照変換はありません。

2番目の拡張メソッドを削除すると、この行はコンパイルに成功します。マッチングの拡張メソッドがあり

public static void PopulateRelations<T>(this List<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
{ 
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); 
} 

public static void PopulateRelations<T>(this IList<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
{ 
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); 
} 

できないのはなぜコンパイラのフィギュアから:

も(...コレクションのすべての可能なタイプのために)トランポリンの方法を書いても動作することに注意してください?もっと混乱して、別の名前空間にメソッドの1つを入れ、その名前空間を組み込むと、なぜコンパイルが成功するのでしょうか?これを解決するために何かできることはありますか?

+0

オーバーロードの解決には制約がありません。さらに、メソッド関数の中で 'T'が必要ないように思えますが、単に' IDbObject'を使うのではなく、あなたの拡張に 'T'を使う必要がありますか? – Jcl

+0

拡張メソッドの中で 'T'を必要としない場合、単に@ jakub-lortzのようなメソッドを変更するだけで、@ Jclの人々は' T'を使うと非常に混乱します。 – mijail

+0

謝罪 - 私は答えにそれをしない理由を含めなかった。その理由は、 'Transaction.PopulateRelations'もT型をとり、' typeof'を呼び出すため、Tが 'IDBObject'だけでなく具体的​​な型である必要があるからです。 –

答えて

2

一般的な制約はメソッドシグネチャの一部ではないため、を選択すると、TIEnumerable<T>よりも多く導出されるためです。

List<ITable> list; 
PopulateRelations(list, something); // Not calling as extension method to more clear 

listため試合を直接あなたの一般的なタイプはIDBObjectに拘束されているList<ITable>

+0

Eric Lippertがこれに関連する役に立つブログ記事を書いています:http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx –

+0

では、なぜ名前空間を並べ替えると、コンパイルが成功したのですか? –

+0

@MichaelParkerどのように名前空間を注文しますか? –

0

で:

例は、これらの2つの方法の間で:呼び出すとき

public static void PopulateRelations(this List<ITable> obj, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    // Do something 
} 

public static void PopulateRelations(this IEnumerable<ITable> objects, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    // Do something 
} 

最初のものが選ばれます拡張メソッドを非汎用にすることができます:

public static void PopulateRelations(this IDBObject obj, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    //... 
} 

public static void PopulateRelations(this IEnumerable<IDBObject> objects, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    //... 
} 

コンパイルエラーを解決します。

+0

彼らは、Tを使用し、T上で 'typeof'を呼び出すPopulateRelationsと呼ばれるトランザクションの汎用メソッドに委任しているので、あまり意味がありません。私はこれを私のオリジナルのポストには含めませんでした。 –

関連する問題