Dziś krótki post o ciekawej konwersji, która ma miejsce podczas przekazywania delegat. Rozważmy kod:
List<int> numbers = new List<int>(new[] {3, 5, 464}); numbers.ForEach(n => Console.WriteLine(n));
ForEach to przykład metody, która jako parametr wejściowy przyjmuje wskaźnik na funkcję:
public void ForEach(Action<T> action)
Okazuje się, że powyższą linię można zapisać nieco prościej a mianowicie:
List<int> numbers = new List<int>(new[] {3, 5, 464}); numbers.ForEach(Console.WriteLine);
Na pierwszy rzut oka wydaje się to dziwne – WriteLine wygląda jak właściwość a nie metoda\delegata. Jak to działa? C# po prostu przeszukuje wszystkie przeładowania WriteLine i dopasowuje je do definicji delegaty. Foreach jako parametr przyjmuje typ Action<T>, zatem kompilator będzie starał się znaleźć odpowiednie przeładowanie WriteLine a następnie przekaże za nas parametry (w tym przypadku liczbę całkowitą).
Nie jest to żadna “specjalna konwersja”. Jeśli napiszemy własną metodę przyjmującą delegatę, podobnie będziemy mogli przekazywać po prostu nazwy metod. Nie jest to funkcjonalność ograniczona wyłącznie do ForEach.
Jak sprawa wygląda od strony dobrych praktyk? Niektórym może się nie spodobać, że wywołanie nie wygląda jak lambda ale jak przekazanie zwykłej właściwości. Moim zdaniem to nazwa powinna określać czy jest to właściwość czy metoda – właściwości nie mogą być czasownikami a metody z kolei muszą być (a dokładniej w trybie rozkazującym). Niestety gorzej sprawa wygląda z parametrami. Jeśli jest ich kilka warto opisać je za pomocą nazwy. Podanie samej metody nic nie mówi jakie parametry przekazujemy a to z kolei może ułatwić nam zrozumienie logiki. Wszystko zależy więc od konkretnego przykładu.