2012-06-27 16 views
12

Quindi, un metodo abbastanza comune estensione per IEnumerable, Run:Perché la risoluzione di sovraccarico di C# non funziona tra Func <T,T> e Action <T>?

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    foreach (var item in source) 
    { 
     action(item); 
     yield return item; 
    } 
} 

Quando provo ad usare quella con, ad esempio, DbSet.Add:

invoice.Items.Run(db.InvoiceItems.Add); 
// NB: Add method signature is 
// public T Add(T item) { ... } 

... il compilatore si lamenta che ha il tipo di ritorno sbagliato, perché si aspetta un metodo di annullamento. Quindi, aggiungere un sovraccarico per Run che prende una Funz invece di Azione:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action) 
{ 
    return source.Select(action).ToList().AsEnumerable(); 
} 

E ora il compilatore si lamenta che "La chiamata è ambigua tra i seguenti metodi ..."

Quindi la mia domanda è, in che modo l'overload di Action del metodo Run può causare ambiguità quando non è valido per il gruppo di metodi?

+0

Qual è la firma di 'db.InvoiceItems.Add'? – leppie

+0

pubblico T Aggiungi (elemento T) {...} –

+0

Risposta breve: 'x => x.ToString()' questo lambda deve semplicemente richiamare ToString o invocare ToString e restituire il risultato?In altre parole, questo lambda dovrebbe essere gestito come una funzione o un'azione? Il compilatore non può prendere questa decisione per te quindi quindi un errore. – Polity

risposta

5

Questo è già stato spiegato da Eric e Jon nelle risposte a this question. Per farla breve - questo è il modo in cui funziona il compilatore C#; precisamente, quando si tratta di conversione metodo gruppo decidere cosa delegato che sarà convertita utilizza la risoluzione di sovraccarico, che non tiene tipi restituiti in conto:

Il principio è che la determinazione convertibilità metodo gruppo richiede la selezione di un metodo da un gruppo di metodi che utilizza la risoluzione di sovraccarico e la risoluzione di sovraccarico non considera i tipi di ritorno.

Nel tuo esempio compilatore vede sia Action<T> e Func<T, T> come migliore corrispondenza per Add. Ciò consente di aggiungere fino a due possibili scelte e, poiché richiede uno, viene emesso un errore appropriato.

0

sovraccarico prova nel modo giusto:

public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
    Func<TSource, TDest> action) 
{ 
return source.Select(action).ToList(); 
} 
+0

Non fa la minima differenza. –

+0

È necessario rimuovere la .ToList qui, per evitare di eseguire la query –

+0

@SteveB Il nome del metodo è Esegui, ma non LazyRun –

0

non posso rispondere perché ma per risolvere l'ambiguità si può lanciare in modo esplicito la funzione:

invoice.Items.Run((Func<T,T>)db.InvoiceItems.Add); 

o utilizzare un lambda

invoice.Items.Run(x => db.InvoiceItems.Add(x)); 
0

Non so perché non possa risolverlo automaticamente, ma ecco due soluzioni alternative:

// with T replaced with the actual type: 
invoice.Items.Run((Func<T, T>)db.InvoiceItems.Add); 
invoice.Items.Run(new Func<T, T>(db.InvoiceItems.Add)); 

Perché avete bisogno di questi metodi comunque? Cosa c'è di sbagliato con:

foreach (var item in invoice.Items) 
    db.InvoiceItems.Add(item); 

La leggibilità di questo è molto meglio. A meno che tu non abbia una buona ragione per aver bisogno del metodo Run, ti consiglio di non usarlo. Da quello che ho visto, non c'è una tale ragione, almeno per il sovraccarico di Action<T>.

+0

Esecuzione è un'operazione di dichiarazione in stile funzionale comune e non sono d'accordo la forma foreach è più leggibile. Inoltre, una volta inserite le parentesi, sono quattro righe anziché una. Lo sto facendo per una mezza dozzina di raccolte di bambini; aggiungi una riga vuota tra ciascuna e quella è ~ 30 righe di codice, il che rende il metodo di contenimento troppo lungo, quindi refactor ogni foreach out in un metodo separato. Poi mi rifaccio il metodo per mantenere le cose ASCIUTTE e, ciao, ho comunque un metodo Run. –

+1

@MarkRendle Il tuo 'Run()' non è molto funzionale. Gran parte della programmazione funzionale sta scrivendo funzioni che non hanno effetti collaterali. E 'Run()' è utile * solo * per gli effetti collaterali. Sono d'accordo con Tim su questo: 'foreach' è più leggibile e l'uso di metodi come' Run() 'non è una buona pratica. – svick

+0

@svick Sono rispettosamente in disaccordo. –

Problemi correlati