Passer au contenu du pied de page
Iron Academy Logo
Apprendre le C#
Apprendre le C#

Autres catégories

Maîtriser les générateurs C#

Tim Corey
38m 51s

Les génériques en C# sont devenus une partie intégrante du langage depuis leur introduction, offrant de nombreux avantages même si leur fonctionnement n'est pas entièrement compris par tous les développeurs. Dans sa vidéo "Comment créer des génériques en C#, y compris les nouvelles fonctionnalités", Tim Corey explique pourquoi les génériques sont importants, comment les créer et démontre leurs applications pratiques.

Cet article fournit un guide complet sur les génériques C#, offrant de précieuses indications tirées de la vidéo de Tim Corey sur le sujet. Elle couvre les principes fondamentaux des génériques, y compris la sécurité des types, les avantages en termes de performances et les applications pratiques. L'article explore également la création de méthodes, de classes et d'interfaces génériques, ainsi que l'application de contraintes aux génériques. En outre, elle met l'accent sur des cas d'utilisation réels et sur l'importance de l'utilisation des génériques pour écrire un code efficace et sans danger pour les types.

Introduction

Les génériques C# offrent un moyen puissant de créer un code flexible, réutilisable et sûr en termes de type en permettant aux développeurs de définir des classes, des méthodes, des interfaces et des collections qui fonctionnent avec n'importe quel type de données. En utilisant une classe générique ou une méthode générique, les développeurs peuvent définir un paramètre de type générique (par exemple, T) qui peut représenter n'importe quel type de données. Cela élimine le besoin de duplication de code et améliore la réutilisation du code tout en garantissant la sécurité des types à la compilation. Les classes de collection génériques telles que List et Dictionary<TKey, TValue> permettent une gestion efficace de différents types de données, tandis que les interfaces génériques et les délégués génériques permettent la création de types génériques personnalisés qui peuvent travailler avec plusieurs paramètres de type. En exploitant les génériques, les développeurs peuvent maximiser l'efficacité du code et minimiser les inconvénients des classes non génériques. Cette flexibilité permet de créer un code plus réutilisable sans sacrifier les performances ou la sécurité des types.

Tim at (0:00) introduit le sujet en soulignant l'utilisation généralisée des génériques en C#. Il vise à expliquer pourquoi les génériques sont essentiels et à démontrer comment les créer et les utiliser efficacement.

Création du projet

Tim commence par créer une nouvelle application console nommée "GenericsDemoApp" afin de se concentrer uniquement sur la démonstration des génériques sans aucune distraction au niveau de l'interface utilisateur. Il met en place le projet en utilisant .NET 8 et Visual Studio 2022.

Bases de la générique

Tim à (2:22) commence par un exemple utilisant la classe List pour illustrer le concept de génériques. Les génériques permettent de spécifier le type d'éléments qu'une collection peut contenir, ce qui garantit la sécurité des types et évite les erreurs d'exécution.

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

La List garantit que seuls les entiers peuvent être ajoutés à la liste des nombres et que seules les chaînes peuvent être ajoutées à la liste des chaînes.

Sécurité et efficacité de la typographie

Tim souligne l'importance de la sécurité de type fournie par les génériques. Le compilateur vérifie les types au moment de la conception, ce qui permet d'éviter les erreurs de type et de garantir une exécution sûre du code. Les génériques permettent également d'éviter les opérations de "boxing" et de "unboxing", ce qui permet d'obtenir un code plus efficace.

Inefficacité des collections non génériques

Pour démontrer l'inefficacité de l'utilisation de collections non génériques, Tim crée une liste et montre comment elle peut contenir différents types d'objets.

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

Il explique que l'utilisation de collections non génériques peut conduire à des incohérences de type et à des inefficacités dues à la mise en boîte et à la mise hors boîte.

Comparaison des Performances

Tim (6:15) compare les performances d'une List et d'une List pour ajouter un million d'éléments. Il utilise un chronomètre pour mesurer le temps écoulé pour chaque opération.

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");
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");

Les résultats montrent que l'ajout d'entiers à une List est significativement plus rapide que l'ajout à une List en raison de l'absence de boxing et de unboxing.

Comparaison de performance

Création d'une méthode de vérification de type

Tim (10:14) montre comment créer une méthode générique appelée TypeChecker. Cette méthode vérifie le type d'une valeur donnée et l'imprime, illustrant ainsi la flexibilité et la puissance des génériques.

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

La méthode TypeChecker utilise l'opérateur typeof pour déterminer le type du paramètre générique T et imprime à la fois le type et la valeur.

Utilisation de la méthode du vérificateur de type

Tim fournit des exemples d'appel de la méthode TypeChecker avec différents types d'arguments.

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

En passant différents types à la méthode TypeChecker, Tim montre comment les génériques peuvent gérer différents types de données de manière transparente.

Créer une classe générique : Meilleure liste

Tim (16:25) passe à la création d'une classe générique nommée BetterList. Cette classe encapsule une liste d'un type spécifique et fournit des fonctionnalités supplémentaires.

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");
    }
}
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");
    }
}

La classe BetterList comprend une List privée et une méthode AddToList qui ajoute une valeur à la liste et imprime un message indiquant l'ajout.

Utilisation de la classe Better List

Tim fournit des exemples d'utilisation de la classe BetterList avec différents types.

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

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

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

Dans ces exemples, BetterList gère une liste d'entiers, tandis que BetterList gère une liste d'objets PersonRecord. Chaque ajout à la liste déclenche un message de console indiquant la valeur ajoutée.

Création d'une interface générique

Tim (21:48) présente l'idée d'une interface générique appelée IImportance. Cette interface définit une méthode pour déterminer laquelle de deux valeurs est la plus importante.

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

Mise en œuvre de l'interface générique

Tim montre comment mettre en œuvre cette interface pour différents types. Il commence par l'implémentation d'un entier.

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

Ensuite, il met en œuvre l'interface pour les chaînes de caractères, en utilisant la longueur des chaînes pour déterminer l'importance.

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

Ces implémentations démontrent comment la même interface peut être appliquée à différents types avec une logique spécifique pour chaque type.

Application des contraintes aux génériques

Tim (25:21) explique comment appliquer des contraintes aux génériques, afin de s'assurer qu'ils remplissent certaines conditions. Par exemple, un type générique peut être contraint d'avoir un constructeur vide ou d'implémenter une interface particulière.

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

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

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

Ces contraintes permettent de s'assurer que le type générique répond aux critères nécessaires, ce qui permet d'éviter les erreurs d'exécution et d'améliorer la sécurité des types.

Microsoft's Implementation of INumber (en anglais)

Tim explique comment Microsoft utilise l'interface INumber pour limiter les opérations numériques. Cela permet d'effectuer des opérations arithmétiques telles que l'addition et la soustraction sur des types génériques.

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

En contraignant le type générique T à INumber, cela garantit que le type prend en charge les opérations numériques.

Utilisation des génériques avec différents types numériques

Tim à (33:55) développe la classe MathOperations pour démontrer comment les génériques peuvent être utilisés avec différents types numériques, tels que les doubles et les décimales.

Tim montre comment créer des instances de MathOperations pour les entiers et les doubles :

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
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

Ceci démontre la flexibilité des génériques, permettant à différents types numériques d'être gérés de manière transparente au sein d'une même classe.

Gestion des différents types numériques

Tim souligne l'importance de la sécurité des types en montrant qu'il n'est pas possible de mélanger différents types numériques. Par exemple, essayer d'ajouter un double à un entier entraînera une erreur à la compilation.

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

Éviter les frais généraux liés à la conversion des types

Tim explique les avantages de l'utilisation des génériques pour éviter la surcharge associée à la conversion des types. Par exemple, convertir des nombres entiers en doubles pour des opérations mathématiques, puis revenir à des nombres entiers, peut s'avérer coûteux. L'utilisation de génériques permet d'effectuer des opérations directes sur les types natifs, tout en préservant les performances et la précision.

Les génériques en pratique

Tim conseille la prudence lors de l'utilisation des génériques, recommandant aux développeurs de les utiliser de manière appropriée et d'éviter d'en faire un usage excessif. Il souligne les avantages des génériques, tels que la sécurité des types, la réduction de la mise en boîte et de la mise hors boîte, la vérification au moment de la compilation et l'amélioration de la lisibilité du code.

Il souligne également que les génériques sont fréquents dans les collections telles que List et Dictionary<TKey, TValue>, ainsi que dans les frameworks de journalisation qui peuvent gérer divers types sans avoir besoin de connaître les spécificités à l'avance.

Conclusion

L'exploration détaillée par Tim Corey des génériques avancés en C# fournit des indications précieuses sur leurs applications pratiques et leurs avantages. Si vous souhaitez approfondir votre compréhension des génériques et voir des exemples concrets en action, ne manquez pas de regarder la vidéo détaillée de Tim Corey sur C# Generics. Ses explications claires et ses démonstrations pratiques vous aideront à bien saisir les concepts et à les appliquer efficacement dans vos propres projets.

Hero Worlddot related to Maîtriser les générateurs C#
Hero Affiliate related to Maîtriser les générateurs C#

Gagnez plus en partageant ce que vous aimez

Vous créez du contenu pour les développeurs travaillant avec .NET, C#, Java, Python ou Node.js ? Transformez votre expertise en revenu supplémentaire !

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi