2015-09-23 22 views
5

Sto scrivendo due metodi di estensione. Uno per lavorare su un singolo oggetto, e un altro per lavorare su una collezione di oggetti. Quando si chiama il metodo di estensione, il compilatore C# sembra confondersi su quale utilizzare e fallisce la compilazione.Compilazione del metodo di estensione C#/verifica della compatibilità non riuscita in base all'ordine dei namespace

Più sorprendentemente, se sposto i metodi di estensione in spazi dei nomi diversi, anche se includo entrambi gli spazi dei nomi nel callsite, la compilazione fallisce solo se gli spazi dei nomi sono in ordine alfabetico in ordine particolare - la modifica degli spazi dei nomi causa la compilazione.

Ecco il codice:

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

Questa è la linea callsite che non riesce la compilazione:

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

Fails con CS0311 errore:

tipo 'System.Collections. Generic.List 'non può essere utilizzato come parametro di tipo' T 'nel tipo generico o nel metodo' Granta.MI.DBObjectExtensions.PopulateRelations (T, params Granta.MI. RelationToPrefetch [])'. Non esiste alcuna conversione implicita del riferimento da "System.Collections.Generic.List" a "Granta.MI.IDBObject".

Si noti che questa riga ha esito positivo la compilazione se si elimina il 2 ° metodo di estensione.

Si noti inoltre che la scrittura di metodi trampolino (per ogni possibile tipo di raccolta ...) funziona anche:

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

Perché non è possibile la figura del compilatore che c'è un metodo di estensione di corrispondenza? E più confusamente, se metto uno dei metodi in uno spazio dei nomi diverso e includo quello spazio dei nomi, perché la compilazione ha avuto successo? C'è qualcosa che posso fare per risolvere questo problema?

+0

La risoluzione del sovraccarico non tiene conto dei vincoli. Inoltre, non sembra che sia necessario 'T' nella funzione del metodo, perché è necessario usare' T' per il proprio interno invece di usare 'IDbObject'? – Jcl

+0

@Jcl le persone si confondono molto spesso con l'uso di 'T', come hai detto se non hai bisogno di' T' all'interno del metodo di estensione semplicemente cambia i metodi come nella risposta @ jakub-lortz. – mijail

+0

Scuse - Non ho incluso il motivo per non farlo nella mia risposta.Il motivo è che 'Transaction.PopulateRelations' accetta anche un tipo T e poi chiama' typeof' quindi ho bisogno che T sia un tipo concreto non solo 'IDBObject'. –

risposta

2

I vincoli generici non fanno parte della firma del metodo, quindi il compilatore sceglie perché T è più derivato di IEnumerable<T>.

esempio, tra questi 2 metodi:

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 
} 

Il primo si è scelto al momento della chiamata:

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

perché list partite direttamente con List<ITable>

+0

Eric Lippert ha scritto un utile post sul blog relativo a questo: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx –

+0

Allora perché la compilazione ha avuto successo quando ho riordinato i namespace? –

+0

@MichaelParker come si ordinano gli spazi dei nomi? –

0

I suoi tipi generici sono costretti a IDBObject , quindi potresti semplicemente rendere i metodi di estensione non generici:

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

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

Risolve l'errore di compilazione.

+0

Non proprio, perché delegano ad un metodo generico sulla transazione chiamato PopulateRelations che usa la T e chiama 'typeof' su T. Mi rendo conto di non averlo incluso nelle mie originali post-scuse. –