2015-06-17 29 views
9

Co się nazywa, gdy metoda, która przyjmuje wyrażenie lambda jako parametr, taki jak Enumerable.Where, jest wywoływana bez faktycznego zadeklarowania parametru zmiennej lub metody w wyrażeniu?C# wyrażeń lambda bez deklaracji zmiennej/parametru?

Na przykład, jestem zaznajomieni z tej składni wyrażeń lambda:

public string GetDigits(string input) 
{ 
    return new String(input.Where(i => Char.IsDigit(i)).ToArray()); 
} 

Jednak byłem zaskoczony find out, że może to być również zapisane jako:

public string GetDigits(string input) 
{ 
    return new String(input.Where(Char.IsDigit).ToArray()); 
} 

Co się dzieje w ten drugi fragment, gdzie metoda Char.IsDigit() jest (pozornie) wywoływana z niejawnym parametrem? Jak nazywa się ta składnia?

+0

ReSharper sugeruje również uproszczoną składnię, jeśli dotyczy. –

+0

Niż to już nie jest wyrażenie lambda, ale przekazanie delegata jako argumentu innej metody. –

+1

Patrzysz na * grupę metod *. –

Odpowiedz

16

Metody nie akceptują parametrów lambdas. Przyjmują delegatów jako parametry.Lambda to tylko jeden sposób na stworzenie delegata.

Innym sposobem jest dostarczenie grupy metod, tak jak w drugim przykładzie, który można przekonwertować na delegata.

Podobny sposób polega na użyciu funkcji anonimowej metody. Zostało to mniej więcej zastąpione lambdami, gdy zostały dodane, więc nie widzisz tego zbyt wiele. Twój przykład za pomocą tej składni byłoby:

Func<char, bool> predicate = delegate(char c) { return Char.IsDigit(c); }; 

Jeszcze innym sposobem byłoby stworzenie delegata korzystając Delegate.CreateDelegate. (To nie jest coś, co widzisz bardzo często.)

Ostatnim sposobem jest mieć zmienną delegata, którą otrzymałeś z innego miejsca. (To gdzieś indziej stworzyli delegata przy użyciu jednego z tych innych opcji.)

Co się dzieje w tym drugim fragmencie, gdzie metoda Char.IsDigit() jest (podobno) jest zwany z niejawny parametr? Jak nazywa się ta składnia?

To nie jest ani. O to chodzi. Próbujemy utworzyć delegata. Delegat to obiekt, który śledzi metodę, która ma zostać wywołana, oraz obiekt, na którym powinien się on uruchamiać. Następnie możesz wywołać delegata i wywołać metodę, która została użyta do jego utworzenia. Więc jesteś nie dzwoniąc pod numer IsDigit, tworzysz delegata, który wskazuje na metodę IsDigit i będzie ją wywoływać za każdym razem, gdy delegat zostanie wywołany.

Kiedy używasz lambdy, tworzysz nową metodę, prawdopodobnie w nowej klasie (z których żadna nie ma nazwy, do której możesz się odnosić, ale będzie miała jedną w środowisku wykonawczym) i treść tego anonimowego metoda zadzwoni pod numer IsDigit. Następnie lambda przechodzi do delegata wskazującego na tę anonimową metodę, która zachowuje semantykę innego przykładu posiadania metody, która po wywołaniu wywołuje anonimową metodę, która w swojej implementacji wywołuje IsDigit. Dodaje dodatkową warstwę pośrednią (która może, ale nie musi być po prostu zoptymalizowana w czasie wykonywania), aby osiągnąć to samo.

+0

* Gdy używasz lambda, tworzysz nową klasę * Tylko jeśli lambda zostanie zamknięta przez zmienną lub pole instancji, w przeciwnym razie zostanie zbuforowana wewnątrz klasy wywołującej. –

+0

Czy można użyć metody niestatycznej jako delegata, która nie jest dostępna bezpośrednio? Więc jeśli 'Char.IsDigit' nie był statyczny, czy mógłbyś go użyć jako delegata? –

+1

@TimSchmelter Możesz użyć grupy metod niestatycznej metody, aby utworzyć delegata, tak. Jedynym wymaganiem, aby grupa metod mogła zostać przekształcona w delegata, jest unikalne najlepsze przeciążenie grupy metod, które pasuje do podpisu danego uczestnika. Jeśli żaden z przeciążeń nie ma prawidłowego podpisu lub jest dwuznaczność, w której jest "najlepszy", będzie to błąd. – Servy

8

Ponieważ kompilator niejawnie rzutować grupę metody do delegata, jeżeli stwierdzi jedną metodę, która pasuje oczekiwany podpis, w tym przypadku delegata biorąc jeden char jako wejście i powrót do bool.

10

Podpis Enumerable.Where jest:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate 
) 

to:

input.Where(i => Char.IsDigit(i)) 

jest równoważne formie pisemnej:

Func<char, bool> temp = i => Char.IsDigit(i); 
input.Where(temp); 

dlatego tworzy anonimową funkcję z parametrem i który wywołuje Char.IsDigit.

to:

input.Where(Char.IsDigit) 

odpowiada

Func<char, bool> temp = Char.IsDigit; 
input.Where(temp); 

równoważnej:

Func<char, bool> temp = new Func<char, bool>(Char.IsDigit); 
input.Where(temp); 

więc tworzy pełnomocnika do Char.IsDigit, a następnie przekazuje je do input.Where.

Drugi usuwa "man-in-the-middle" (funkcja anonimowa). W tym konkretnym przypadku jest "legalny", ponieważ parametr anonimowej funkcji i jest przekazywany "tak jak jest" do Char.IsDigit. Byłoby inaczej, gdyby to było:

input.Where(i => !Char.IsDigit(i)) 

w tym przypadku, nie można usunąć man-in-the-middle (funkcja anonimowa).

Nie ma nazwy dla tego wszystkiego (lub można nazwać pierwszy "tworzenie i przekazywanie delegata do anonimowej funkcji", a drugi "tworzenie i przekazywanie delegata utworzonego z grupy metod" ... ale oni nie są pięknymi hasłami, są raczej opisem tego, co robisz)

+0

"Nie ma na to nazwy". Istnieje jednak: jest to * grupa metod *. Zobacz także http://stackoverflow.com/questions/886822/what-is-a-method-group-in-c –

+0

@JeroenVannevel Nie, grupą metod jest 'Char.IsDigit' w' input.Where (Char .IsDigit) '. Tworzenie delegata z grupy metod (czyli co się robi) nie ma nazwy (lub jej nazwa dokładnie tworzy delegata z grupy metod) – xanatos

+0

@TimSchmelter Skradziony :-) – xanatos

5

Twój Where oczekuje Func<char, bool>, który jest delegatem na metodach, które wymagają char argumentu i zwraca bool. Wszystko, co pasuje do tego delegata, jest ważnym argumentem dla tego Where.

  • lambda napisałeś początkowo pasuje do tej delegata typ wnioskowania: kompilator spodziewa się, że i jest char, na podstawie ogólnej parametr przeliczalnego źródła - i wnioskuje typ zwracany jako bool, ponieważ to właśnie wywołanie metody wyrażenie wewnątrz lambda powróciłoby.
  • Sama metoda Char.IsDigit również pasuje do tego. Odwoływanie się do tej metody jest więc kolejnym ważnym sposobem wyrażania tej samej rzeczy. Nazywa się to grupą metod .

Semantyczna równoważność tych dwóch możliwych argumentów Where również sens, jeśli wziąć pod uwagę, że dla każdego wyrażenia lambda, kompilator generuje sposób anonimowy, a następnie przechodzi że metody anonimowej gdzie oczekiwano delegat.

Aby to zilustrować, rozważmy oryginalny fragment:

Where(i => Char.IsDigit(i)) 

Powyższy gets lowered by the compiler do:

bool AnAnonymousMethod(char i) 
{ 
    return Char.IsDigit(i); 
} 

a następnie:

Where(AnAnonymousMethod) 

Jak widać, składnia lambda (w przypadku, gdy nie masz przechwyconych zmiennych, jak tutaj) jest to po prostu cukier syntaktyczny do napisania a metoda nonymous, a następnie użycie grupy metod tej nowo napisanej metody jako argumentu wszędzie tam, gdzie oczekuje się zgodnego uczestnika.

Powiązane problemy