2012-12-05 6 views
6

Recentemente ho scritto questo e sono rimasto sorpreso che si compila:Generics in C# con più tipi generici porta a permesso e consentiti ambiguità

public class MyGeneric<U, V> { 
    MyGeneric(U u) { ... } 
    MyGeneric(V v) { ... } 
    public void Add(U u, V v) { ... } 
    public void Add(V v, U u) { ... } 
} 

Se io uso questa classe come segue, ottengo un "riferimento costruttore ambiguo" e una "invocazione ambigua" se chiamo Aggiungi.

var myVar = new MyGeneric<int, int>(new MyIntComparer()); 

Ovviamente, non c'è ambiguità quando uso int e doppia come tipi generici, tranne ovviamente quando uso sia interi, che sarebbe anche assegnare sia in doppio.

var myVar = new MyGeneric<int, double>(new MyIntComparer()); 
myVar.Add(3, 5); 

Quindi ho pensato che fosse permesso anche il seguente, ma sorprendentemente ho ricevuto un errore. Perché non è consentito compilare il seguente?

public interface IMyInterface<T, S> { 
    void Add(T t, S s); 
} 

public class MyGeneric<U, V> : IMyInterface<U, V>, IMyInterface<V, U> { 
    public MyGeneric(U u) { } 
    public MyGeneric(V v) { } 
    void IMyInterface<U, V>.Add(U u, V v) { ... } 
    void IMyInterface<V, U>.Add(V v, U u) { ... } 
} 

Indipendentemente se uso implementazione dell'interfaccia implicita o esplicita, il compilatore rileva che

'MyGeneric < U, V >' non può attuare sia IMyInterface 'IMyInterface < U, V >' e' < V, U > 'perché possono unificare per alcune sostituzioni dei parametri di tipo

E perché è il primo allo vuoi scrivere?

+2

Sebbene [questi] (http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx) [due] (http: // blogs.msdn.com/b/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx) i post del blog parlano del caso in cui un metodo generico e un metodo non generico potrebbero finire con la stessa firma per determinati argomenti di tipo, potrebbero anche applicarsi al caso dell'argomento a due tipi. (La risposta data al motivo per cui questo è permesso è essenzialmente "Lo abbiamo permesso in C# 2.0 ed è troppo tardi per cambiarlo ora") – Rawling

+0

Grazie per questi collegamenti, è la spiegazione dell'implementazione del compilatore che stavo cercando – AlexH

+0

@Rawling, grazie per il link interessante – Andreas

risposta

4

1- Perché non è consentito compilare il seguente testo?

Parte della risposta è in questo post: Why does the C# compiler complain that "types may unify" when they derive from different base classes?

La sezione 13.4.2 delle C# 4 stati di specifica:

Le interfacce implementate da una dichiarazione di tipo generico devono rimanere unica per tutti possibili tipi costruiti Senza questa regola, sarebbe impossibile determinare il metodo corretto per chiamare determinati tipi costruiti .

2- E perché il primo è autorizzato a scrivere?

Il compilatore eseguire il tipo di controllo generico al momento della compilazione, la sezione 7.4.3.5 del C# 4 stati di specifica:

Mentre firme come dichiarato deve essere univoco, è possibile che sostituzione di argomenti di tipo risultati in firme identiche. In tali casi, le regole di overclocking della risoluzione di sovraccarico sopra saranno selezionare il membro più specifico.Gli esempi che seguono mostrano sovraccarichi che sono validi e non validi secondo questa regola:

interface I1<T> {...} 
interface I2<T> {...} 
class G1<U> 
{ 
    int F1(U u);     // Overload resulotion for G<int>.F1 
    int F1(int i);     // will pick non-generic 
    void F2(I1<U> a);    // Valid overload 
    void F2(I2<U> a); 
} 
class G2<U,V> 
{ 
    void F3(U u, V v);   // Valid, but overload resolution for 
    void F3(V v, U u);   // G2<int,int>.F3 will fail 
    void F4(U u, I1<V> v);  // Valid, but overload resolution for 
    void F4(I1<V> v, U u);  // G2<I1<int>,int>.F4 will fail 
    void F5(U u1, I1<V> v2); // Valid overload 
    void F5(V v1, U u2); 
    void F6(ref U u);    // valid overload 
    void F6(out V v); 
} 
+0

Grazie per la spiegazione dettagliata. È vero che la creazione di MyGeneric implementerebbe sostanzialmente la stessa interfaccia due volte ed è per questo che si lamenta delle sostituzioni dei parametri. – Andreas

1

Fa parte della specifica del linguaggio, come spiegato nella risposta accettata qui:

Why does the C# compiler complain that "types may unify" when they derive from different base classes?

Sezione 13.4.2 degli stati delle specifiche C# 4:

Se possibile, costruito tipo creato da C, dopo che gli argomenti tipo sono stati sostituiti in L, perché due interfacce in L sono identiche, la dichiarazione di C non è valida. Le dichiarazioni di vincoli non vengono prese in considerazione quando si determinano tutti i possibili tipi di costruzione.

Credo che la differenza tra il vostro due esempio è che il secondo utilizza interfacce (controllati per duplicati, per le specifiche di lingua), ma il primo utilizza tipi (non controllati per i duplicati, nonostante l'ambiguità causando potenzialmente come avete visto) .

+0

Grazie, sì è vero, questo fondamentalmente deriva dalla stessa interfaccia due volte. – Andreas