Extension Methods w C# — wywołania łańcuchowe (Fluent API)

W poprzednim artykule poznałeś podstawy metod rozszerzających — składnię, słowo kluczowe this i zasady ich definiowania. Tutaj skupiamy się na jednym, konkretnym zastosowaniu: budowaniu wywołań łańcuchowych (method chaining), czyli serii operacji zapisanych jako jeden, płynny ciąg — dokładnie tak, jak działa LINQ (list.Where(...).Select(...).ToList()).
Warunek konieczny: zwracaj ten sam typ
Klucz do wywołań łańcuchowych jest prosty: gdy metoda rozszerzająca zwraca wartość tego samego typu, co jej argument wejściowy, możesz od razu wywołać na wyniku kolejną metodę — rozszerzającą albo standardową. Każde kolejne wywołanie “doczepia się” do poprzedniego, tworząc łańcuch.
Przykład: StringBuilder
Załóżmy, że mamy klasę StringBuilder i chcemy dodać metody rozszerzające, które umożliwią nam łańcuchowe dodawanie tekstu, formatowanie itp.
using System;
using System.Text;
public static class StringBuilderExtensions
{
public static StringBuilder AppendLineChainable(this StringBuilder sb, string value)
{
return sb.AppendLine(value);
}
public static StringBuilder AppendFormatted(this StringBuilder sb, string format, params object[] args)
{
return sb.AppendFormat(format, args);
}
}
class Program
{
static void Main()
{
StringBuilder sb = new StringBuilder();
sb.AppendLineChainable("Hello, World!")
.AppendFormatted("The current date and time: {0}", DateTime.Now)
.AppendLineChainable("Goodbye, World!");
Console.WriteLine(sb.ToString());
}
}
AppendLineChainable i AppendFormatted zwracają ten sam StringBuilder, na którym operują — dzięki temu kolejne wywołanie może “doczepić się” bezpośrednio do wyniku poprzedniego, bez zapisywania wyników pośrednich do zmiennych.
Łańcuchowe operacje na liście liczb
Drugi, dydaktyczny przykład — metody rozszerzające dla List<int> pozwalające filtrować, transformować (mapować) i formatować dane w jednym, czytelnym łańcuchu:
Filter: filtruje listę według określonego kryterium,Transform: przekształca (mapuje) każdy element listy,AsString: konwertuje listę na sformatowany ciąg znaków.
using System;
using System.Collections.Generic;
using System.Linq;
public static class ListExtensions
{
public static List<int> Filter(this List<int> list, Func<int, bool> predicate)
{
return list.Where(predicate).ToList();
}
public static List<int> Transform(this List<int> list, Func<int, int> transformer)
{
return list.Select(transformer).ToList();
}
public static string AsString(this List<int> list)
{
return string.Join(", ", list);
}
}
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
string result = numbers.Filter(n => n % 2 == 0) // filtruje parzyste liczby
.Transform(n => n * 2) // podwaja każdą liczbę
.AsString(); // konwertuje listę na string
Console.WriteLine(result);
}
}
Zwróć uwagę: to dokładnie ten sam wzorzec, na którym zbudowane jest LINQ — Filter/Transform/AsString odpowiadają koncepcyjnie wbudowanym Where/Select/string.Join. Pisząc własne metody rozszerzające tego typu, w praktyce najczęściej lepiej sięgnąć po gotowe LINQ — ale zrozumienie, jak je zbudować samemu, pokazuje mechanikę, która stoi za każdym łańcuchem .Where(...).Select(...).
Mutowanie vs zwracanie nowej instancji
Warto świadomie rozróżnić dwa style łańcuchowania, bo mylenie ich bywa źródłem trudnych do znalezienia błędów:
- Styl mutujący (jak
AppendLineChainablepowyżej) — metoda modyfikuje przekazany obiekt i zwraca tę samą referencję. Wywołaniesb.AppendLineChainable(...)zmienia oryginalnysb, nawet jeśli nie przypiszesz wyniku do zmiennej. - Styl niemutujący (jak
Filter/Transformpowyżej, wzorem LINQ) — metoda zwraca nową instancję, a oryginał pozostaje bez zmian. Jeśli zignorujesz zwróconą wartość (nie przypiszesz jej do zmiennej), efektywnie nic się nie stanie — częsty błąd początkujących przy LINQ, gdzielista.Where(...)bez.ToList()i przypisania nie zmienia oryginalnej listy.
Dla własnych metod rozszerzających w stylu łańcuchowym warto trzymać się konsekwentnie jednego podejścia i jasno je udokumentować — mieszanie obu stylów w jednym łańcuchu wywołań łatwo prowadzi do nieoczekiwanego zachowania.
Zalety wywołań łańcuchowych
- Czytelność — kolejne operacje są zapisane w sposób płynny i ciągły, bez zmiennych pośrednich.
- Modularność — każda operacja to osobna, niezależna metoda, którą łatwo testować w izolacji.
- Elastyczność — metody rozszerzające dodają nową funkcjonalność bez modyfikacji oryginalnej klasy.
Wywołania łańcuchowe najlepiej sprawdzają się przy budowaniu ciągów znaków, operacjach na kolekcjach i płynnych interfejsach konfiguracyjnych (fluent builders) — wszędzie tam, gdzie seria kolejnych, powiązanych operacji jest naturalnie sekwencyjna.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
8 comments
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
Jesteś przyjacielem ponieważ jesteś węzłem w swobodnej kwerendzie, który łączy, lecz nie zniewala i wicąż na ziemie powala. Sensei H. Oshiro.
Henri Millon Montherlant wyznawal maksyme, ze Przyjazń miedzy kobietami jest niczym wiecej jak paktem o nieagresji.
What is a blogging site where people give a lot of quick feedback?
exchange of opinion, feedback is very important
Branie czegos na serio przez dlugi czas utrudnia zycie. – Andre Gide
Zycie jest podrlza, ktora prowadzi do domu.
good job, thank you