IRONSOFTWAREHOME

Opanowanie generyków w C#

Nauka zaawansowanych generików w C#

Tim Corey

38m 51s

Generyki w C# stały się integralną częścią języka od momentu ich wprowadzenia, oferując liczne korzyści, nawet jeśli ich działanie nie jest w pełni zrozumiałe przez wszystkich deweloperów. W swoim filmie "How To Create Generics in C#, Including New Features" Tim Corey wyjaśnia, dlaczego generyki są ważne, jak je tworzyć oraz demonstruje ich praktyczne zastosowania.

Ten artykuł oferuje kompleksowy przewodnik po generykach w C#, zawierający cenne informacje z filmu Tima Coreya na ten temat. Omówiono tutaj podstawy generyków, w tym bezpieczeństwo typów, korzyści wydajnościowe oraz praktyczne zastosowania. Artykuł obejmuje również tworzenie metod generycznych, klas, interfejsów oraz stosowanie ograniczeń na generyki. Dodatkowo podkreśla rzeczywiste przypadki użycia oraz znaczenie stosowania generyków w celu pisania efektywnego, bezpiecznego pod względem typów kodu.

Wprowadzenie

Generyki w C# oferują potężny sposób na tworzenie elastycznego, wielokrotnego zastosowania i bezpiecznego pod względem typów kodu, pozwalając deweloperom definiować klasy, metody, interfejsy i kolekcje działające z dowolnym typem danych. Korzystając z klasy generycznej lub metody generycznej, deweloperzy mogą zdefiniować parametr typu generycznego (np. T), który może reprezentować dowolny typ danych. Eliminuje to koniecznosc duplikowania kodu i zwieksza mozliwosc ponownego wykorzystania kodu, zapewniajac jednoczesnie bezpieczenstwo typow w czasie kompilacji. Klasy kolekcji ogolnych, takie jak List i Dictionary<TKey, TValue>, pozwalaja na wydajne zarzadzanie roznego rodzaju danymi, podczas gdy interfejsy ogolne i delegaty ogolne umozliwiaja tworzenie niestandardowych typow ogolnych, ktore moga pracowac z wieloma parametrami typow. Wykorzystując generyki, deweloperzy mogą maksymalizować wydajność kodu i minimalizować wady klas niegenerycznych. Ta elastyczność umożliwia tworzenie bardziej wielokrotnego zastosowania kodu bez poświęcenia wydajności czy bezpieczeństwa typów.

Tim (0:00) wprowadza temat, podkreślając powszechne użycie generyków w C#. Jego celem jest wyjaśnienie, dlaczego generyki są niezbędne oraz jak je tworzyć i efektywnie używać.

Tworzenie projektu

Tim zaczyna od stworzenia nowej aplikacji konsolowej o nazwie "GenericsDemoApp", aby skupić się wyłącznie na demonstrowaniu generyków bez rozpraszania uwagi przez interfejs użytkownika. Ustawia projekt korzystając z .NET 8 i Visual Studio 2022.

Podstawy Generyków

Tim na (2:22) zaczyna od przykladu uzycia klasy List, aby zilustrowac pojecie generycznosci. Generyki pozwalają określać typ elementów, które kolekcja może zawierać, zapewniając bezpieczeństwo typów i unikając błędów w czasie wykonania.

List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };

List zapewnia, ze tylko liczby calkowite moga byc dodawane do listy liczb, a tylko lancuchy znakow moga byc dodawane do listy lancuchow.

Bezpieczeństwo typów i wydajność

Tim podkreśla znaczenie bezpieczeństwa typów zapewnianego przez generyki. Kompiator sprawdza typy już na etapie projektowania, zapobiegając niezgodności typów i zapewniając bezpieczne wykonanie kodu. Generyki również eliminują potrzebę stosowania boxingu i unboxingu, co prowadzi do bardziej efektywnego kodu.

Nieskuteczność Kolekcji Niegenerycznych

Aby zademonstrować nieskuteczność stosowania kolekcji niegenerycznych, Tim tworzy Listę i pokazuje, jak może ona przechowywać różne typy obiektów.

List<object> objects = new List<object> { "Tim", 4, 3.6m };

Wyjaśnia, że stosowanie kolekcji niegenerycznych może prowadzić do niezgodności typów i nieskuteczności z powodu boxingu i unboxingu.

Porownanie wydajności

Tim (6:15) przeprowadza porównanie wydajności między korzystaniem z List<object> i List<int>, aby dodać milion elementów. Używa stopera, aby zmierzyć czas wykonania każdej operacji.

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

for (int i = 1; i <= 1_000_000; i++)
{
    objects.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");

stopwatch.Restart();

for (int i = 1; i <= 1_000_000; i++)
{
    numbers.Add(i);
}

stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");

Wyniki pokazują, że dodawanie liczb całkowitych do List<int> jest znacznie szybsze niż do List<object> z powodu braku boxingu i unboxingu.

Porównanie wydajności

Tworzenie Metody Sprawdzającej Typ

Tim (10:14) demonstruje, jak stworzyć metodę generyczną o nazwie TypeChecker. Ta metoda sprawdza typ podanej wartości i wypisuje go, pokazując elastyczność i moc generyków.

public static void TypeChecker<t>(T value)
{
    Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}
C#

Metoda TypeChecker używa operatora typeof do określenia typu parametru generycznego T i wypisuje zarówno typ, jak i wartość.

Korzystanie z Metody Sprawdzającej Typ

Tim prezentuje przykłady wywołania metody TypeChecker z różnymi typami argumentów.

TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1

Przekazując różne typy do metody TypeChecker, Tim pokazuje, jak generyki mogą bezproblemowo obsługiwać różne typy danych.

Tworzenie Klasy Generycznej: Better List

Tim (16:25) przechodzi do stworzenia klasy generycznej o nazwie BetterList. Ta klasa enkapsuluje listę określonego typu i zapewnia dodatkową funkcjonalność.

public class BetterList<t>
{
    private List<t> data = new List<t>();

    public void AddToList(T value)
    {
        data.Add(value);
        Console.WriteLine($"{value} has been added to the list");
    }
}
C#

Klasa BetterList zawiera prywatna liste List oraz metode AddToList, ktora dodaje wartosc do listy i wyswietla wiadomosc wskazujaca na dodanie.

Korzystanie z Klasy Better List

Tim przedstawia przykłady korzystania z klasy BetterList z różnymi typami.

BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);

BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));

W tych przykładach, BetterList<int> zarządza listą liczb całkowitych, podczas gdy BetterList<PersonRecord> zarządza listą obiektów PersonRecord. Każde dodanie do listy powoduje wyświetlenie komunikatu konsolowego wskazującego dodaną wartość.

Tworzenie Generycznego Interfejsu

Tim (21:48) wprowadza pomysł generycznego interfejsu o nazwie IImportance. Ten interfejs definiuje metodę do określenia, która z dwóch wartości jest ważniejsza.

public interface IImportance<t>
{
    T MostImportant(T a, T b);
}
C#

Implementacja Generycznego Interfejsu

Tim pokazuje, jak zaimplementować ten interfejs dla różnych typów. Zaczyna od implementacji dla liczb całkowitych.

public class EvaluateImportance : IImportance<int>
{
    public int MostImportant(int a, int b)
    {
        return a > b ? a : b;
    }
}

Następnie implementuje interfejs dla napisów, używając długości napisów do określenia ważności.

public class EvaluateStringImportance : IImportance<string>
{
    public string MostImportant(string a, string b)
    {
        return a.Length > b.Length ? a : b;
    }
}

Te implementacje pokazują, jak ten sam interfejs może być zastosowany do różnych typów z określoną logiką dla każdego typu.

Stosowanie Ograniczeń na Generyki

Tim (25:21) wyjaśnia, jak stosować ograniczenia na generyki, zapewniając, że spełniają one określone warunki. Na przykład, typ generyczny może być ograniczony do posiadania konstruktóra bezparametrowego lub implementacji określonego interfejsu.

public class SampleClass<t> where T : new()
{
    // Class implementation
}

public class SampleClassWithInterface<t> where T : IImportance<t>
{
    // Class implementation
}
C#

Te ograniczenia pomagają zapewnić, że typ generyczny spełnia niezbędne kryteria, zapobiegając błędom w czasie wykonania i zwiększając bezpieczeństwo typów.

Implementacja INumber przez Microsoft

Tim omawia, jak Microsoft używa interfejsu INumber do ograniczania operacji numerycznych. Pozwala to na operacje arytmetyczne takie jak dodawanie i odejmowanie na typach generycznych.

public class MathOperations<t> where T : INumber<t>
{
    public T Add(T x, T y)
    {
        return x + y;
    }
}
C#

Ograniczajac typ ogolny T do INumber, zapewnia sie, ze typ obsluguje operacje numeryczne.

Korzystanie z Generyków z Różnymi Typami Numerycznymi

Tim (33:55) rozszerza klasę MathOperations, aby zademonstrować, jak generyki mogą być używane z różnymi typami numerycznymi, takimi jak double i decimal.

Tim pokazuje, jak tworzyć instancje MathOperations dla liczb całkowitych i double:

MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5

MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8

To demonstruje elastyczność generyków, pozwalając różnym typom numerycznym być obsługiwanym bezproblemowo w tej samej klasie.

Obsługa Różnych Typów Numerycznych

Tim podkreśla znaczenie bezpieczeństwa typów, pokazując, że nie można mieszać różnych typów numerycznych. Na przykład, próba dodania double do liczby całkowitej spowoduje błąd w czasie kompilacji.

// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));

Unikanie Kosztów Konwersji Typów

Tim wyjaśnia korzyści płynące z korzystania z generyków, by unikać kosztów związanych z konwersją typów. Na przykład, konwersja liczb całkowitych na double dla operacji matematycznych i z powrotem na liczby całkowite może być kosztowna. Korzystanie z generyków pozwala na bezpośrednie operacje na natywnych typach, zachowując wydajność i precyzję.

Generyki w Praktyce

Tim zaleca ostrożność przy korzystaniu z generyków, sugerując, aby deweloperzy używali ich odpowiednio i unikali nadmiernego stosowania. Podkreśla korzyści z generyków, takie jak bezpieczeństwo typów, redukcja boxingu i unboxingu, sprawdzanie w czasie kompilacji i zwiększona czytelność kodu.

Rowniez zwraca uwage, ze generycznosc jest powszechna w kolekcjach takich jak List i Dictionary<TKey, TValue>, jak rowniez w frameworkach do logowania, ktore moga obslugiwac rozne typy bez potrzeby znajomosci szczegolow z gory.

Wnioski

Szczegółowe omówienie zaawansowanych generyków w C# przez Tima Coreya dostarcza cennych spostrzeżeń na temat ich praktycznych zastosowań i korzyści. Jeśli chcesz pogłębić swoje rozumienie generyków i zobaczyć rzeczywiste przykłady w użyciu, koniecznie obejrzyj szczegółowy film Tima Coreya na temat Generyków w C#. Jego jasne wyjaśnienia i praktyczne demonstracje pomogą ci w pełni zrozumieć koncepcje i skutecznie je zastosować w swoich własnych projektach.

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

Uzyskaj natychmiast swój darmowy 30-dniowy Klucz Testowy.

Your trial license will be sent to your email address

Brak ograniczeń. 100% dostępności. Bez karty kredytowej.

bullet_checkedNie wymaga karty kredytowej ani tworzenia kontaBrak ograniczeń. 100% dostępności. Bez karty kredytowej.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Otrzymaj swoją Konsultację Bez Zobowiązań
Wypełnij poniższy formularz lub wyślij e-mail na sales@ironsoftware.com
Twoje dane zawsze będą utrzymywane w tajemnicy.
Zaufane przez miliony inżynierów na całym świecie
Logotypy klientów Iron Software
Otrzymaj swój darmowy Klucz Próbny na 30 dni natychmiast.
Nie wymaga karty kredytowej ani tworzenia konta