Perché C # non può digitare il tipo da questo caso apparentemente semplice e ovvio

Dato questo codice:

class C { C() { Test(A); // fine Test((string a) => {}); // fine Test((Action)A); // fine Test(A); // type arguments cannot be inferred from usage! } static void Test(Action a) { } void A(string _) { } } 

Il compilatore si lamenta che Test(A) non riesce a capire che T sia una string .

Questo mi sembra un caso abbastanza semplice, e giuro che ho invocato un’inferenza molto più complicata in altre utilità generiche e funzioni di estensione che ho scritto. Cosa mi manca qui?

Aggiornamento 1: questo è nel compilatore C # 4.0. Ho scoperto il problema in VS2010 e il suddetto esempio proviene da una replica di caso più semplice realizzata in LINQPad 4.

Aggiornamento 2: aggiunti altri esempi all’elenco di ciò che funziona.

 Test(A); 

Ciò non riesce perché l’unico metodo applicabile ( Test(Action) ) richiede l’inferenza di tipo e l’algoritmo di inferenza del tipo richiede che ogni argomento sia di qualche tipo o sia una funzione anonima. (Questo fatto è dedotto dalla specifica dell’algoritmo di inferenza del tipo (§7.5.2)) Il gruppo di metodi A non è di alcun tipo (anche se è convertibile in un tipo di delegato appropriato) e non è una funzione anonima.

 Test(A); 

Ciò avviene correttamente, con la differenza che l’inferenza di tipo non è necessaria per eseguire il bind di Test e il gruppo di metodi A è convertibile nel tipo di parametro delegato richiesto void Action(string) .

 Test((string a) => {}); 

Questo succede, con la differenza che l’algoritmo di inferenza del tipo prevede la fornitura di funzioni anonime nella prima fase (§7.5.2.1). I parametri e i tipi restituiti della funzione anonima sono noti, quindi è ansible eseguire un’inferenza esplicita del tipo di parametro e viene quindi effettuata una corrispondenza tra i tipi nella funzione anonima ( void ?(string) ) e il parametro type nel tipo delegato del parametro del metodo Test ( void Action(T) ). Nessun algoritmo è specificato per i gruppi di metodi che corrisponderebbero a questo algoritmo per le funzioni anonime.

 Test((Action)A); 

Questo succede, con la differenza che il parametro A gruppo di metodi non tipizzato viene gettato su un tipo, consentendo quindi all’inferenza di tipo di Test di procedere normalmente con un’espressione di un particolare tipo come unico argomento del metodo.

Non riesco a pensare in teoria perché non sia ansible tentare la risoluzione del sovraccarico sul gruppo di metodi A Quindi, se viene trovata una singola associazione migliore, al gruppo di metodi potrebbe essere assegnato lo stesso trattamento di una funzione anonima. Ciò è particolarmente vero in casi come questo in cui il gruppo metodo contiene esattamente un candidato e non ha parametri di tipo. Ma il motivo per cui non funziona in C # 4 sembra essere il fatto che questa funzione non è stata progettata e implementata. Data la complessità di questa caratteristica, la narità della sua applicazione e l’esistenza di tre facili aggeggi, non ho intenzione di trattenere il respiro per questo!

Penso che sia perché è un’inferenza in due fasi:

  • Si deve dedurre che si desidera convertire A in un delegato generico

  • Deve inferire quale dovrebbe essere il tipo di parametro delegato

Non sono sicuro se questa sia la ragione, ma la mia impressione è che un’inferenza in due passaggi non sia necessariamente facile per il compilatore.


Modificare:

Solo un sospetto, ma qualcosa mi sta dicendo che il primo passo è il problema. Il compilatore deve capire di convertirsi in un delegato con un numero diverso di parametri generici , e quindi non può dedurre i tipi dei parametri.

Questo mi sembra un circolo vizioso.

Test metodo di Test prevede un parametro di tipo delegato creato dal tipo generico Action . Passi invece in un gruppo di metodi : Test(A) . Ciò significa che il compilatore deve convertire il parametro in un tipo delegato ( conversione del gruppo metodo ).

Ma quale tipo di delegato? Per conoscere il tipo di delegato è necessario conoscere T. Non lo abbiamo specificato esplicitamente, quindi il compilatore deve inferirlo per capire il tipo di delegato.

Per dedurre i parametri di tipo del metodo, è necessario conoscere i tipi degli argomenti del metodo, in questo caso il tipo delegato. Il compilatore non conosce il tipo di argomento e quindi fallisce.

In tutti gli altri casi è evidente il tipo di argomento:

 // delegate is created out of anonymous method, // no method group conversion needed - compiler knows it's Action Test((string a) => {}); // type of argument is set explicitly Test((Action)A); 

o il parametro type è specificato esplicitamente:

 Test(A); // compiler knows what type of delegate to convert A to 

PS più su inferenza di tipo

Stai passando il nome del Metodo A. Il framework .Net PUOI convertirlo in Action , ma è implicito e non si prenderà la responsabilità per questo.

Ma ancora, un nome di metodo NON è un object esplicito di Action<> . E quindi non dedurrà il tipo come un tipo di Action .

Potrei sbagliarmi, ma immagino che la vera ragione per cui C # non può inferire il tipo è dovuta al sovraccarico del metodo e all’ambiguità che ne deriva. Ad esempio, supponiamo di avere i seguenti metodi: void foo (int) e void foo (float) . Ora se scrivo var f = foo . Quale foo dovrebbe scegliere il compilatore? Allo stesso modo, lo stesso problema si verifica con il tuo esempio usando Test(foo) .