2009-05-14 6 views
5

Vedere le quattro linee del Go() il metodo di seguito:La differenza tra creazione delegato implicito ed esplicito (con e senza farmaci generici)

delegate void Action<T>(T arg); 
delegate void Action(); 

void DoSomething<T>(Action<T> action) 
{ 
    //... 
} 

void DoSomething(Action action) 
{ 
    //... 
} 

void MyAction<T>(T arg) 
{ 
    //... 
} 

void MyAction() 
{ 
    //... 
} 

void Go<T>() 
{ 
    DoSomething<T>(MyAction<T>); // throws compiler error - why? 
    DoSomething(new Action<T>(MyAction<T>)); // no problems here 
    DoSomething(MyAction); // what's the difference between this... 
    DoSomething(new Action(MyAction)); // ... and this? 
} 

Si noti che l'errore del compilatore generato dalla prima chiamata è: Gli argomenti tipo per il metodo 'Azione (T)' non possono essere dedotti dall'utilizzo. Prova a specificare esplicitamente gli argomenti del tipo.

+0

Non riesco a capire come questo possa compilare (in isolamento): vuoto DoSomething (Azione azione) { // ...} Dal momento che T non è dichiarato ... E 'il tutto all'interno una classe generica? –

+0

Sì, sto modificando il mio esempio ora. Avrebbe dovuto leggere Go () {... –

+0

Sono io ma quando compilo il codice che hai qui compila senza errori.Supponendo che inserisco il codice nella classe non generica. Sto usando C# 3.5 SP1 – JoshBerke

risposta

13

Non c'è alcuna differenza tra MyAction e new Action(MyAction) (quando sono entrambe valide) diverso dal primo non funzionerà in C# 1. Questo è un implicit method group conversion. Ci sono volte che questo non è applicabile, in particolare quando il compilatore non riesce a capire quale tipo di delegato si desidera, ad es.

Delegate foo = new Action(MyAction); // Fine 
Delegate bar = MyAction; // Nope, can't tell target type 

Questo entra in gioco nella tua domanda perché entrambi i metodi coinvolti sono sovraccarichi. Questo porta a mal di testa, in pratica.

Per quanto riguarda i generici, è interessante. I gruppi di metodi non amano molto l'inferenza di tipo C# 3 - Non sono sicuro se ciò sarà migliorato in C# 4 o no. Se si chiama un metodo generico e specificare l'argomento tipo, inferenza di tipo funziona abbastanza bene - ma se si prova a farlo il contrario, non riesce:

using System; 

class Test 
{ 
    static void Main() 
    { 
     // Valid - it infers Foo<int> 
     DoSomething<int>(Foo); 
     // Valid - both are specified 
     DoSomething<int>(Foo<int>); 
     // Invalid - type inference fails 
     DoSomething(Foo<int>); 
     // Invalid - mismatched types, basically 
     DoSomething<int>(Foo<string>); 
    } 

    static void Foo<T>(T input) 
    { 
    } 

    static void DoSomething<T>(Action<T> action) 
    { 
     Console.WriteLine(typeof(T)); 
    } 
} 

inferenza di tipo in C# 3 è molto complicato, e funziona bene nella maggior parte dei casi (in particolare è ottimo per LINQ) ma fallisce in pochi altri. In un mondo ideale, sarebbe più facile capire e più potenti nelle versioni future ... vedremo!

+0

Cheers! Ho accettato l'altra risposta, ma il tuo è comunque utile. +1 –

3

La creazione implicita delegato non generico è solo zucchero sintattico, quindi il compilatore genera esattamente lo stesso codice per

DoSomething(MyAction); 

e

DoSomething(new Action(MyAction)); 

come si può dedurre il tipo del delegato direttamente dal contesto del metodo argomenti &.

Con il delegato generico, è necessario specificare il tipo di delegato a causa di covarianza e controvarianza (vedere http://msdn.microsoft.com/en-us/library/ms173174(VS.80).aspx per i dettagli) - il T in Action può essere un supertipo della T nel metodo e sarà comunque accettato come un metodo delegato. Quindi, è necessario specificare la T nel delegato in modo esplicito in quanto il compilatore non può capirlo da solo.

+0

Cheers, ora capisco. Link a covarianza vs controvarianza articolo per coloro che non capiscono: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) –

+0

Direi che è più a che fare con le limitazioni di inferenza di tipo rispetto alla contravarianza, personalmente. Se leggi la sezione delle specifiche sull'inferno del tipo (non riesco a ricordarlo subito) vedrai che i gruppi di metodi in pratica non hanno molte possibilità. –

1

Solo un sidenote .. Per qualche motivo questo funziona in VB.

Sembra che l'implementazione del preprocessore in C# e VB differisca quando arrivo a convertire il Methodgroup/adderessof in un delegato.