2012-06-27 10 views
12

also eine recht häufige Erweiterungsmethode für IEnumerable, Run:Warum funktioniert die Überladungsauflösung von C# nicht zwischen Func <T,T> und 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; 
    } 
} 

Wenn ich versuche, das zu verwenden, mit zum Beispiel DbSet.Add:

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

... der Compiler beschwert mich, dass Es hat den falschen Rückgabetyp, weil es eine ungültige Methode erwartet. So fügen Sie eine Überlastung für Run, die eine Func statt Aktion nimmt:

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

Und nun der Compiler beschwert sich, dass „Der Anruf zwischen den folgenden Methoden nicht eindeutig ist ...“

Also meine Frage ist, Wie kann die Action-Überladung der Run-Methode zu Mehrdeutigkeiten führen, wenn sie für die Methodengruppe nicht gültig ist?

+0

Was ist die Signatur von 'db.InvoiceItems.Add'? – leppie

+0

public T Add (T item) {...} –

+0

Kurze Antwort: 'x => x.ToString()' Soll dieser Lambda einfach ToString aufrufen oder ToString aufrufen und sein Ergebnis zurückgeben?Mit anderen Worten, sollte dieses Lambda wie eine Funktion oder eine Aktion behandelt werden? Der Compiler kann diese Entscheidung nicht für Sie treffen, daher ein Fehler. – Polity

Antwort

5

Dies wurde bereits von Eric und Jon in Antworten auf this question erklärt. Lange Rede, kurzer Sinn - so funktioniert der C# -Compiler; Gerade, wenn es mit Methode Gruppenumwandlung zu tun zu entscheiden, welche Delegierten wird es Überladungsauflösung verwendet werden umgewandelt, die nicht zurück nehmen Arten in Rechnung:

Das Prinzip dabei ist, dass die Konvertierbarkeit Bestimmungsverfahren Gruppe erfordert ein Verfahren der Auswahl aus einer Methodengruppe, die die Überladungsauflösung verwendet, und die Überladungsauflösung berücksichtigt keine Rückgabetypen.

In Ihrem Beispiel Compiler sieht sowohl Action<T> und Func<T, T> als beste Übereinstimmung für Add. Dies führt zu zwei möglichen Auswahlmöglichkeiten, und da es einen erfordert, wird ein entsprechender Fehler ausgegeben.

0

try Überlastung in der rechten Weise:

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

macht nicht den geringsten Unterschied. –

+0

Sie sollten die .ToList hier entfernen, um die Abfrage zu vermeiden –

+0

@SteveB Method Name ist Run, aber nicht LazyRun –

0

kann ich nicht beantworten, warum aber die Zweideutigkeit lösen können Sie ausdrücklich Ihre Funktion Stimmen:

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

oder eine Lambda verwenden

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

Ich weiß nicht, warum es nicht automatisch lösen kann, aber hier sind zwei Problemumgehungen:

Warum brauchen Sie diese Methoden überhaupt? Was ist los mit:

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

Die Lesbarkeit dieser ist viel besser. Wenn Sie keinen guten Grund haben, die Run Methode zu verwenden, würde ich empfehlen, sie nicht zu verwenden. Von dem, was ich gesehen habe, gibt es keinen solchen Grund, zumindest für die Action<T> Überlastung.

+0

Run ist eine gemeinsame funktionale Stil deklarative Operation, und ich stimme nicht, dass die foreach Form ist lesbarer. Sobald Sie die Klammern angebracht haben, sind es vier statt einer Zeile. Ich mache das für ein halbes Dutzend Kindersammlungen; addiere eine Leerzeile zwischen jeder Zeile und das sind ~ 30 Codezeilen, was die Methode zu lang ist, also refactoriere ich jede foreach in eine separate Methode um. Dann refaktoriere ich diese Methode, um die Dinge trocken zu halten und, hey presto, ich habe sowieso eine Run-Methode. –

+1

@MarkRendle Ihr 'Run()' ist nicht sehr funktional. Ein großer Teil der funktionalen Programmierung besteht darin, Funktionen zu schreiben, die keine Nebenwirkungen haben. Und 'Run()' ist nützlich * nur * für Nebenwirkungen. Ich stimme Tim darin zu: 'foreach' ist besser lesbar und die Verwendung von Methoden wie' Run() 'ist keine sehr gute Übung. – svick

+0

@svick Ich stimme respektvoll nicht zu. –

Verwandte Themen