2009-06-08 12 views
5

Non capisco perché il compilatore non possa risolvere il sovraccarico corretto da usare qui. (codice di seguito) Esiste solo una versione di Add() appropriata: BigFoo è un IFoo e non implementa IEnumerable dove T è un IFoo. Ma insiste nel riferire un'ambiguità. Qualche idea? Ho provato ad aggiungere un secondo parametro di tipo generico: Aggiungi dove T: IFoo dove U: IEnumerable. Ma poi il sovraccarico è completamente ignorato anche per uso legittimo.C# overload generico - Il compilatore non può determinare la chiamata corretta

So che posso aggirare questo problema con il cast e specificando parametri di tipo generico ma a quel punto ho sconfitto lo scopo di avere un sovraccarico. Potresti mettere in discussione il sovraccarico, ma la semantica mi sembra corretta: il comportamento che sto implementando nella mia classe è che sia per Add() sia per aggiungere l'oggetto all'ingrosso come una singola voce nella raccolta. (Il secondo add() non dovrebbe essere un AddRange().)

namespace NS 
{ 
    interface IFoo { } 

    class BigFoo : IFoo, IEnumerable<int> 
    { 
    public IEnumerator<int> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    } 

    class FooContainer 
    { 
    public void Add(IFoo item) { } 
    public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
    } 

    class DemoClass 
    { 
    void DemoMethod() 
    { 
     BigFoo bigFoo = new BigFoo(); 
     FooContainer fooContainer = new FooContainer(); 
     // error CS0121: The call is ambiguous between the following methods or properties: 
     // 'NS.FooContainer.Add(NS.IFoo)' and 
     // 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)' 
     fooContainer.Add(bigFoo); 
    } 
    } 
} 

risposta

6

risoluzione di sovraccarico generico non tiene vincoli in considerazione, in modo che ritiene la versione Add<T> sia applicabile, inferire T=int.

Entrambi i metodi sono applicabili e nessuno dei due è decisamente migliore dell'altro, in quanto non vi è alcuna conversione tra IEnumerable<int> e IFoo. Mentre i metodi generici sono considerati "meno specifici" dei metodi non generici, questo diventa rilevante solo quando i tipi di parametri sono identici dopo la sostituzione degli argomenti di tipo, che in questo caso non sono.

+0

Jeff Richter concorda "il compilatore C# preferisce un match più esplicito nel corso di un incontro generico" Display ("Jeff") sarebbe partita Display (String) su display (T) si applicano solo – Gishu

+1

Le regole tie-breaking se i tipi di parametri formali sono IDENTICI. Ad esempio, se si ha M (int x) e M (T t), il primo è migliore di M (int t). –

+0

Ah, grazie Eric. È bello avere le specifiche online e contribuire;) Modificherà in modo appropriato. –

0

Il compilatore deve essere abbastanza intelligente da riconoscere che BigFoo non può essere trasmesso su IEnumerable<IFoo>, ma non lo è. Si vede semplicemente che è un IEnumerable<T> e ritiene che si tratti di un potenziale candidato sovraccarico (anche se il file contsint che hai definito impone che T deve essere IFoo e int non può essere trasmesso a IFoo). Anche se è scomodo, non è un grosso problema. Basta lanciare bigFoo al IFoo e il compilatore sarà felice:

fooContainer.Add((IFoo)bigFoo); 

In alternativa, potete fare il vostro sovraccarico generico Aggiungere più brutte:

public void Add<T, U>(U group) 
    where T : IFoo 
    where U : IEnumerable<T> 
{ 
} 

In entrambi i casi, si ha più di battitura, il secondo elimina soluzione la necessità di gettare le chiamate a Add, ma si dovrà dichiarare in modo esplicito il tipo sulle chiamate verso il generico aggiuntivo (che finisce per essere più codice:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo); 
1

In FooContainer, sul secondo "Aggiungi" si vincola T a essere di tipo IFoo. BigFoo implementa l'interfaccia IFoo, quindi si adatta alla definizione Aggiungi (anche se in realtà non lo è, perché non implementa IEnumable <IFoo>).

io non sono sicuro di aver capito tutto quello che vuoi, ma ho il sospetto è questo:

public void Add<T>(T group) where T : IEnumerable<IFoo> { } 

che permetterebbe di aggiungere qualsiasi oggetto T dove T è un insieme enumerabile di oggetti IFoo.

È quello che volevi?

saluti, Richard

+0

limitando T a IEnumerable invece di IFoo continua a non aiutare la risoluzione di sovraccarico. Come ha detto Jon Skeet, il compilatore (sfortunatamente) non terrà conto dei vincoli del conto. –

+0

Ah ... fantastico. Ho imparato qualcosa (altrimenti) nuovo oggi. :-) Grazie per aver trovato il tempo di commentare - ho davvero pensato che il compilatore avrebbe preso in considerazione i vincoli nella clausola "where", anche se non si preoccupava di considerarli altrove nella firma del metodo. –

0

Il problema qui è che i vincoli di tipo generico sono completamente ignorati dal compilatore (si guarda solo a tipi di parametri). Per quanto riguarda il compilatore, l'argomento IEnumerable<T> passato potrebbe anche essere un IEnumerable<IFoo>.

Per informazioni complete su questo argomento, fare riferimento alla sezione 25.6.4 Inferenza di argomenti tipo di C# Language Specification. Si noti che non viene menzionato l'utilizzo dei vincoli di tipo.

+0

Questo non è il posto giusto per cercare le regole specifiche che sono pertinenti a questa domanda. La sezione pertinente nella specifica 3.0 è 7.5.5.1, il bit che inizia "viene eseguita la convalida finale del metodo migliore scelto". Come puoi vedere da questa sezione, il controllo delle violazioni dei vincoli viene eseguito DOPO il controllo per l'unicità del set candidato. –

+0

Ah, giusto. Sicuramente quella sezione delle specifiche suggerisce almeno che i vincoli di tipo generico * non * tengono conto durante il processo di inferenza, comunque? – Noldorin

+0

In effetti, è anche il caso che i vincoli non vengano usati durante l'inferenza. –

0

Interessante .... Ho appena provato il tuo campione. Generics continua a tenermi sulla punta dei piedi.

//1 - First preference 
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); } 
//2 - Second Preference 
public void Add<T>(T item) { Console.WriteLine("Generic Add"); } 
//3 - Third preferences 
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); } 
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists 
public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
Problemi correlati