2012-06-27 16 views
12

Tak, to dość powszechna metoda rozszerzenie dla IEnumerable, Run:Dlaczego funkcja przeciążania C# nie działa między funkcjami <T,T> i 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; 
    } 
} 

Kiedy próbuję użyć tego z, na przykład, DbSet.Add:

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

... kompilator narzeka, że ma zły typ zwrotu, ponieważ oczekuje metody void. Więc dodać przeciążenie dla Run, które ma Func zamiast Działanie:

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

a teraz kompilator narzeka, że ​​„Wywołanie jest niejednoznaczna między następujących metod ...”

więc moje pytanie brzmi, w jaki sposób przeciążenie akcji metodą Run może powodować niejednoznaczność, gdy nie jest poprawne dla grupy metod?

+0

Co to jest podpis "db.InvoiceItems.Add"? – leppie

+0

public T Dodaj (element T) {...} –

+0

Krótka odpowiedź: 'x => x.ToString()' czy to lambda po prostu wywołuje ToString lub wywołuje ToString i zwraca jego wynik?Innymi słowy, czy ta lambda powinna być traktowana jako func czy akcja? Kompilator nie może podjąć tej decyzji, więc wystąpił błąd. – Polity

Odpowiedz

5

Zostało to już wyjaśnione przez Eric i Jon w odpowiedzi na this question. Krótko mówiąc - tak działa kompilator C#; precyzyjnie, gdy ma do czynienia z konwersją grupy metoda decydowania co delegat zostanie skonwertowana do wykorzystuje rozdzielczość przeciążeniem, który nie bierze powrócić rodzajów konta:

Zasada tu jest, że określenie grupy metoda wymienialność wymaga wybierając metodę z grupy metod wykorzystującej rozdzielczość przeciążania, a rozdzielczość przeciążenia nie uwzględnia typów zwracanych.

W przykładzie kompilator widzi zarówno Action<T> i Func<T, T> jak Najlepszy mecz dla Add. Daje to dwa możliwe wybory, a ponieważ wymaga jednego - wydaje odpowiedni błąd.

0

try przeciążenie w odpowiedni sposób:

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

Nie robi najmniejszej różnicy. –

+0

Powinieneś usunąć .ToList tutaj, aby uniknąć wykonywania zapytania –

+0

@ Nazwa metody Metoda jest Uruchomiona, ale nie LazyRun –

0

nie mogę odpowiedzieć dlaczego ale rozwiązać niejednoznaczności można jawnie rzucić swoją funkcję:

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

lub użyć lambda

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

Nie wiem, dlaczego nie może go automatycznie rozwiązać, ale tutaj są dwa obejścia:

// 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)); 

Dlaczego i tak potrzebujesz tych metod? Co jest nie tak z:

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

Czytelność tego jest znacznie lepsza. Jeśli nie masz dobrego powodu, aby potrzebować metody Run, polecam jej używanie. Z tego co widziałem, nie ma takiego powodu, przynajmniej dla przeciążenia Action<T>.

+0

Run jest typową deklaratywną operacją stylu i nie zgadzam się, że formularz foreach jest bardziej czytelny. Co więcej, gdy już wprowadzisz nawiasy klamrowe, są to cztery linie zamiast jednej. Robię to dla pół tuzina kolekcji dziecięcych; dodaj pustą linię pomiędzy każdym a około ~ 30 liniami kodu, co powoduje, że metoda zawierająca zbyt długo jest zbyt długa, więc każdą z tych opcji przekierowuję na osobną metodę. Następnie refactor tej metody, aby utrzymać rzeczy suche i, hej presto, mam metodę Run tak. –

+1

@MarkRendle Twoje 'Run()' nie jest bardzo funkcjonalne. Dużą częścią programowania funkcjonalnego jest pisanie funkcji, które nie mają skutków ubocznych. A 'Run()' jest użyteczne * tylko * dla efektów ubocznych. Zgadzam się z Timem w tej kwestii: 'foreach' jest bardziej czytelny i używanie takich metod jak' Run() 'nie jest dobrą praktyką. – svick

+0

@ Sickick Z całym szacunkiem się nie zgadzam. –

Powiązane problemy