Zrozumienie metod i korzystanie z metod rozszerzeń w C#
W programowaniu w C# metody są niezbędnymi elementami konstrukcyjnymi, które kapsułkują wielokrotnie używany kod i wykonują określone zadania. Mogą one przyjmować parametry, zwracać wartości i być przeciążane, by obsługiwać różne dane wejściowe. Bardziej zaawansowaną koncepcją są metody rozszerzające, które pozwalają programistom na dodawanie funkcjonalności do istniejących typów, w tym tych, nad którymi nie mają kontroli.
Film Tima Coreya 'How To Create Extension Methods in C#' jest doskonałym źródłem informacji. W tym przewodniku omówimy kilka tematów, które porusza Tim:
- Definiowanie i wywoływanie metod
- Parametry i argumenty metod
- Wartości zwracane przez metody
- Przeciążanie metod
- Implementacja metod rozszerzających
Definiowanie i Wywoływanie Metod
Zdefiniowane Metody Instancji
W C# metoda jest zdefiniowana w klasie. Ogólna składnia dla definicji metody obejmuje modyfikator dostępu, typ zwracany, nazwę metody i parametry.
public class SampleClass
{
public void SampleMethod()
{
// Method implementation
}
}
public class SampleClass
{
public void SampleMethod()
{
// Method implementation
}
}
W przykładzie Tima Coreya o 4:05 definiuje on metodę w statycznej klasie, aby utworzyć metodę rozszerzającą. Zdefiniowana metoda to PrintToConsole. Definicja obejmuje całą ogólną składnię, która wyraźnie wyjaśnia, jak zdefiniować metodę z praktycznym przykładem:
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}
Wywoływanie Metody
'Wywołanie metody' mówi programowi, aby wykonał konkretną metodę zdefiniowaną gdzie indziej w kodzie, wykonując z góry zdefiniowaną akcję. Metody są wywoływane za pomocą instancji klasy lub bezpośrednio, jeśli są one metody statyczne. Dla metod rozszerzających wyglądają one, jakby były częścią typu, które rozszerzają. W wideo o 6:18 Tim pokazuje, jak wywołać metodę rozszerzającą z prymitywnym typem danych, tak jak w przypadku jego wbudowanych metod.
string demo = "This is a demo";
demo.PrintToConsole(); // Extension method call
string demo = "This is a demo";
demo.PrintToConsole(); // Extension method call
Parametry i Argumenty Metod
Parametry
Parametry są określone w definicji metody i działają jako miejsca na wartości, które są przekazywane do metody. Możesz to zobaczyć po wywołaniu metody WriteLine, gdzie message jest parametrem.
public void DisplayMessage(string message)
{
Console.WriteLine(message);
}
public void DisplayMessage(string message)
{
Console.WriteLine(message);
}
Ponownie, w przykładzie metody rozszerzającej, który podał Tim Corey o 4:05, message jest parametrem:
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
Argumenty
Argumenty to rzeczywiste wartości przekazane do metody, gdy jest wywoływana.
DisplayMessage("Hello, World!"); // "Hello, World!" is the argument
DisplayMessage("Hello, World!"); // "Hello, World!" is the argument
Gdy Tim Corey wywołuje metodę o 6:20 używając notacji kropkowej z typem string, wartość string jest faktycznie przekazywana jako wartość do metody PrintToConsole:
string demo = "This is a demo";
demo.PrintToConsole(); // "This is a demo" is the argument
string demo = "This is a demo";
demo.PrintToConsole(); // "This is a demo" is the argument
Wartości Zwracane przez Metody
Metody mogą zwracać wartości za pomocą instrukcji return. Typ zwracany jest określony w sygnaturze metody.
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b)
{
return a + b;
}
Czookał metoda rozszerzająca w wideo Tima Coreya nie zwraca żadnej wartości (typ zwracany void), można jednak tworzyć metody rozszerzające ze zwracanymi wartościami. Typ zwracany w przykładzie Tima to void, co oznacza, że metoda nie zwraca żadnej wartości. Poniższy przykład pokazuje, jak zwrócić wartość:
public static int WordCount(this string str)
{
return str.Split(' ').Length;
}
public static int WordCount(this string str)
{
return str.Split(' ').Length;
}
Przeciążanie Metod (11:15)
Przeciążanie metod pozwala na posiadanie wielu metod o tej samej nazwie, ale różnych parametrach. Może być to przydatne przy tworzeniu elastycznych i intuicyjnych API.
public void Display(string message)
{
Console.WriteLine(message);
}
public void Display(int number)
{
Console.WriteLine(number);
}
public void Display(string message)
{
Console.WriteLine(message);
}
public void Display(int number)
{
Console.WriteLine(number);
}
Tim Corey krótko omawia tworzenie wielu metod dla różnych scenariuszy logowania o 11:24, co może być widziane jako przykład przeciążania metod w szerszym znaczeniu. Metoda logowania istnieje dwukrotnie, jedna z jednym parametrem, a druga z dwoma parametrami. Druga metoda logowania o 11:39 jest przeciążoną wersją metody logowania, nadając jej wiele funkcji pod tą samą nazwą.
Implementing Extension Methods in C
Czym są Metody Rozszerzające? (3:13)
Metody rozszerzające pozwalają na dodawanie nowych metod do istniejących typów, bez potrzeby ich modyfikacji lub rekompilacji. Choć są wywoływane tak, jakby były metodami instancji, metody rozszerzające są definiowane jako metody statyczne.
Tworzenie Metody Rozszerzającej
W poprzedniej sekcji 'Definiowanie i Wywoływanie Metod' podkreśliliśmy, jak Tim Corey stworzył metodę rozszerzającą w oddzielnej klasie statycznej i zdefiniował w niej metodę statyczną do użycia jako metoda rozszerzająca. Oto kilka kluczowych punktów, które podkreśla Tim Corey:
- Upewnij się, że zdefiniowałeś oddzielną publiczną klasę statyczną, lub jak mówi Tim, "oznacz klasę jako statyczną, inaczej nie zadziała."
- Gdy tworzysz więcej metod rozszerzających, grupuj je według typu (3:43)
- Definiuj metodę statyczną z pierwszym parametrem poprzedzonym słowem kluczowym
this, określając typ do rozszerzenia (4:58)
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}
Wywoływanie Metody Rozszerzającej (6:18)
Następnie Tim pokazuje, jak wywołać metodę rozszerzającą na tej zmiennej typu string:
demo.PrintToConsole();
demo.PrintToConsole();
Gdy wpiszesz demo i zaczniesz pisać Print, IntelliSense sugeruje metodę PrintToConsole. To jest nowa metoda dodana do typu string.
Jak Działa Wywołanie Metody (6:30)
Tim wyjaśnia, dłączego możesz wywołać demo.PrintToConsole():
- Demo to Typ String: Zmienna
demojest rodzajustring. - Rozszerzony Typ String: Typ
stringzostał rozszerzony o nową metodęPrintToConsole.
Zrozumienie Parametru (6:41)
Chociaż wydaje się, że żadne parametry nie są przekazywane do metody PrintToConsole, Tim wskazuje na domyślne przekazywanie parametru - string demo jest przekazywany jako pierwszy parametr do metody rozszerzającej.
Tim podkreśla, że metody rozszerzające mają jeden parametr mniej w wywołaniu niż w definicji. To dlatego, że pierwszy parametr (typ, który jest rozszerzany) jest domyślny.
Sygnatura Metody Rozszerzającej
Tutaj, this string message oznacza, że metoda rozszerza typ string, a message jest domyślnym parametrem:
public static void PrintToConsole(this string message)
public static void PrintToConsole(this string message)
Uruchamianie Kodu (7:08)
Wreszcie, gdy metoda PrintToConsole jest wywołana, wypisuje ciąg znaków na konsolę:
Console.WriteLine(message);
Console.WriteLine(message);
Tak więc wywołanie demo.PrintToConsole() wypisuje "To jest demo" na konsolę.
Używanie Metod Rozszerzających dla Klas Stron Trzecich
Rozszerzanie Klas Stron Trzecich (10:59)
Tim Corey wyjaśnia, że metody rozszerzające mogą rozszerzać dowolny typ, nawet klasy stron trzecich, których nie można modyfikować bezpośrednio. Na przykład, spójrzmy na klasę SimpleLogger o 11:09.
Tutaj Tim używa hipotetycznej klasy trzeciej strony SimpleLogger, która zapisuje wiadomości do konsoli (11:09). Klasa ma dwie metody:
public class SimpleLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
public void Log(string message, string messageType)
{
Console.WriteLine($"{messageType}: {message}");
}
}
public class SimpleLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
public void Log(string message, string messageType)
{
Console.WriteLine($"{messageType}: {message}");
}
}
Te metody nie są idealne, ponieważ typ wiadomości to prosty string, co może prowadzić do niespójności. Tim sugeruje stworzenie metod rozszerzających, aby poprawić klasę.
Implementacja Spójnych Typów Wiadomości
Używanie metod rozszerzających zapewnia spójność w kodzie, zawsze używając tych samych typów wiadomości i formatowania. Tutaj, o (12:40), Tim tworzy klasę statyczną ExtendSimpleLogger:
public static class ExtendSimpleLogger
{
public static void LogError(this SimpleLogger logger, string message)
{
logger.Log(message, "Error");
}
public static void LogWarning(this SimpleLogger logger, string message)
{
logger.Log(message, "Warning");
}
}
public static class ExtendSimpleLogger
{
public static void LogError(this SimpleLogger logger, string message)
{
logger.Log(message, "Error");
}
public static void LogWarning(this SimpleLogger logger, string message)
{
logger.Log(message, "Warning");
}
}
Uczynienie Wywołań Bardziej Spójnymi
Mając to w ręku, (14:02) teraz jest w stanie wywołać metody rozszerzające na instancji SimpleLogger:
SimpleLogger logger = new SimpleLogger();
logger.LogError("This is an error");
logger.LogWarning("This is a warning");
SimpleLogger logger = new SimpleLogger();
logger.LogError("This is an error");
logger.LogWarning("This is a warning");
To zapewnia, że typy wiadomości są zawsze 'Error' i 'Warning'.
Ulepszanie Formatowania Wyjścia (14:35)
Tim dodaje funkcjonalność do ustawiania koloru tekstu konsoli dla wiadomości o błędach, zapewniając, że się wyróżniają:
public static void LogError(this SimpleLogger logger, string message)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
logger.Log(message, "Error");
Console.ForegroundColor = defaultColor;
}
public static void LogError(this SimpleLogger logger, string message)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
logger.Log(message, "Error");
Console.ForegroundColor = defaultColor;
}
Porównanie z Bezpośrednimi Wywołaniami Metod (17:21)
Tim porównuje to podejście do bezpośredniego wywoływania oryginalnych metod Log, co może prowadzić do niespójności:
logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");
logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");
Podejście to jest podatne na literówki i niespójne formatowanie.
Łączenie Metod Rozszerzających (18:13)
Tim demonstruje, jak metody rozszerzające mogą być łączone, aby kod był bardziej czytelny:
public static void LogInfo(this SimpleLogger logger, string message)
{
logger.Log(message, "Info");
}
public static void SaveToDatabase(this SimpleLogger logger)
{
// Simulate saving to a database
}
public static void LogInfo(this SimpleLogger logger, string message)
{
logger.Log(message, "Info");
}
public static void SaveToDatabase(this SimpleLogger logger)
{
// Simulate saving to a database
}
Teraz możesz łączyć te metody:
logger.LogInfo("Information").SaveToDatabase();
logger.LogInfo("Information").SaveToDatabase();
To sprawia, że kod jest bardziej czytelny i intuicyjny w porównaniu do zagnieżdżonych wywołań metod:
SaveToDatabase(LogInfo(logger, "Information"));
SaveToDatabase(LogInfo(logger, "Information"));
Korzystając z notacji kropkowej i łączenia, intencja kodu jest bardziej przejrzysta i mniej zagnieżdżona.
Rozszerzanie Rzeczy, Których Nie Posiedzisz
O 20:13 Tim Corey wyjaśnia, że metody rozszerzające są idealne do dodawania funkcjonalności do klas, które nie są twoją własnością, takich jak biblioteki stron trzecich. Pozwala to na ulepszenia bez modyfikacji oryginalnego kodu.
Unikanie Zależności
Corey również podkreśla użycie metod rozszerzających do wprowadzenia zależności bez ich bezpośredniego sprzęgania z klasą. Na przykład, dodanie funkcjonalności zapisywania do bazy danych do klasy Person bez wbudowywania logiki bazy danych.
Rozszerzanie Interfejsów
Metody rozszerzające mogą również dotyczyć interfejsów, jak wyjaśniono od 21:30, umożliwiając wielu klasom implementującym interfejs dzielenie tej samej funkcjonalności. To promuje ponowne wykorzystanie kodu i uproszczenie.
Kiedy Nie Używać Metod Rozszerzających
O 23:03 Tim Corey doradza, aby nie nadużywać metod rozszerzających, zwłaszcza z typami prymitywnymi lub dostarczanymi przez Microsoft, aby uniknąć zagracenia i złożoności. Używaj ich oszczędnie i tylko wtedy, gdy oferują wyraźne korzyści.
Zasada Otwartości/Zamkniętości
W sekcji między 24:54-25:40 Tim podkreśla przestrzeganie zasady otwartości/zamkniętości poprzez użycie metod rozszerzających do dodawania nowej funkcjonalności bez modyfikowania istniejącego, stabilnego kodu, co zmniejsza ryzyko wprowadzania błędów.
Najlepsze Praktyki dla Użycia Using
Organizuj metody rozszerzające, grupując je logicznie i umieszczając w oddzielnych przestrzeniach nazw, aby uniknąć konfliktów nazw i ułatwić utrzymanie i debugowanie.
Wnioski
I proszę bardzo – teraz rozumiesz podstawy definiowania i wywoływania metod, obsługi parametrów i wartości zwracanych oraz wykorzystywania przeciążania metod. Dzięki temu możesz budować solidne i elastyczne aplikacje w C#.
Metody rozszerzające, jak wyjaśnia Tim Corey, oferują potężny sposób na ulepszanie istniejących typów i czynią twój kod bardziej czytelnym i utrzymywalnym. Dla bardziej szczegółowych informacji i praktycznych przykładów możesz obejrzeć pełne wideo Tima Coreya na temat How To Create Extension Methods in C#.
