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

Autres catégories

Générer des nombres aléatoires en C#

Tim Corey
10m 05s

Générer des nombres aléatoires en C# semble être une tâche en une ligne, et dans de nombreux cas, c'est le cas. Mais le langage offre plus d'une manière de produire des valeurs aléatoires, et les différences entre elles comptent une fois que vous prenez en compte la sécurité des threads, la reproductibilité, et le cas d'utilisation. Choisir la mauvaise approche peut introduire des bogues subtils dans le code multi-thread ou rendre un défaut signalé impossible à reproduire.

Dans sa vidéo "Générer des nombres aléatoires en C#", Tim Corey passe en revue la classe classique Random, explique pourquoi les valeurs de semence existent, et introduit Random.Shared comme le standard moderne. Nous couvrirons chaque approche avec le raisonnement derrière, afin que vous puissiez choisir la bonne sans deviner. Si vous vous êtes déjà demandé pourquoi il existe plusieurs chemins vers quelque chose qui semble si simple, cet article le décompose.

Configuration de la démo

[0:11 - 0:59] Tim travaille dans une application console s'exécutant sur Visual Studio 2026 (actuellement en aperçu) avec .NET 10. Il note que tout ce qui est démontré ici s'applique également à .NET 9 et Visual Studio 2022, donc vous pouvez suivre avec les outils que vous avez déjà installés.

La mise en page de la démo est une boucle for qui imprime deux valeurs aléatoires côte à côte à chaque itération. Avoir deux sorties fonctionnant en parallèle facilite l'observation pour déterminer si les deux générateurs se comportent indépendamment ou produisent des résultats correspondants, une distinction qui devient importante une fois que les valeurs de graine entrent en jeu.

La classe Random classique

[0:59 - 3:28] L'approche originale pour générer des entiers aléatoires en C# implique de créer une instance de la classe Random :

Random rng1 = new Random();
Random rng2 = new Random();
Random rng1 = new Random();
Random rng2 = new Random();

Chaque instance maintient son propre état interne. Appeler .Next(1, 101) sur l'un ou l'autre produit un entier entre 1 et 100. Tim souligne un détail qui piège les nouveaux venus : la valeur minimale est incluse, mais la maximale ne l'est pas. Si vous voulez des valeurs de 1 à 100, vous passez 1 et 101, pas 1 et 100.

int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);
int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);

L'exécution de l'application confirme que les deux instances produisent des séquences différentes. Ce résultat semble intuitif, mais ce qui se passe lorsque les deux instances partagent le même point de départ raconte une histoire différente.

Un avertissement important concernant cette approche : les instances individuelles de Random ne sont pas sûres pour les threads. Si votre application effectue un traitement parallèle et que plusieurs threads accèdent à la même instance, l'état interne peut devenir corrompu, produisant des zéros ou des valeurs répétées. La pratique sûre consiste à créer une instance par thread. Cette contrainte est l'une des raisons pour lesquelles le langage a ensuite introduit une meilleure alternative.

Valeurs de graine et séquences reproductibles

[3:28 - 6:00] Tim passe ensuite une graine explicite aux deux constructeurs :

Random rng1 = new Random(25);
Random rng2 = new Random(25);
Random rng1 = new Random(25);
Random rng2 = new Random(25);

La sortie change radicalement. Les deux générateurs produisent maintenant des séquences identiques : 79, 16, 25, 90, 50, 41, et ainsi de suite. Les chiffres sont toujours individuellement imprévisibles si vous ne connaissez pas la graine, mais étant donné la même valeur de départ, la progression est déterministe.

Pourquoi quelqu'un voudrait-il cela ? Tim donne un exemple pratique. Imaginez un jeu qui génère des événements aléatoires tout au long d'une session. Un joueur signale un bogue, mais le reproduire semble impossible car les résultats ont été randomisés. Si le jeu enregistre la semence utilisée pour cette session, un développeur peut recréer la chaîne exacte de décisions en initialisant une nouvelle instance de Random avec la même valeur. La même logique s'applique aux scénarios de tests unitaires où vous avez besoin de sorties cohérentes pour écrire des assertions fiables contre un comportement randomisé.

Les instances avec graine vous donnent un hasard contrôlé : une séquence qui semble imprévisible mais peut être rejouée à la demande. Cette capacité est la raison pour laquelle le constructeur classique Random acceptant une semence n'a pas été déprécié, même si une API plus simple existe maintenant.

Random.Shared : le standard moderne

[7:36 - 9:01] À partir de .NET 6, l'approche recommandée pour la plupart des générateurs de nombres aléatoires est Random.Shared :

int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);
int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);

Aucune instanciation n'est impliquée. Random.Shared est une instance statique, sécurisée pour les threads, gérée par le runtime. Vous appelez .Next() (ou toute autre méthode de la classe Random) et recevez une valeur sans vous soucier de la durée de vie de l'objet ou de la concurrence.

Tim exécute la démo deux fois pour prouver le point. La première exécution commence avec 94 et 91 ; le second commence avec 42 et 70. Contrairement à une instance semée, Random.Shared puise dans un état de départ différent chaque fois que le processus est lancé. Vous ne pouvez pas définir une graine, ce qui signifie que vous ne pouvez pas produire une séquence reproductible par le biais de cette API. C'est le compromis: simplicité et sécurité en échange de l'abandon de la relecture déterministe.

Au-delà de .Next(), Random.Shared expose des méthodes pour générer des doubles, remplir des tableaux d'octets et mélanger des collections. Pour la grande majorité du code d'application où vous avez besoin d'une valeur aléatoire rapide sans formalités, cette unique propriété statique remplace le code passe-partout de la gestion de vos propres instances.

Choisir la bonne approche

[9:01 - 9:30] Tim conclut avec un cadre de décision concis. Pour le hasard quotidien (choix d'une valeur, mélange d'une liste, sélection d'un élément aléatoire), Random.Shared est le choix approprié. Il ne nécessite pas de configuration, gère la simultanéité, et se comporte correctement à travers les threads.

Lorsque vous avez besoin d'une série répétable de sorties, que ce soit pour le débogage, les tests ou la répétition de simulation, créez une instance Random dédiée avec une semence connue. Gardez à l'esprit que ces instances ne sont pas sûres à partager entre threads.

Et pour tout ce qui concerne la sécurité (jetons, clés, sels de mot de passe), ni l'une ni l'autre des approches n'est appropriée. Tim dirige les spectateurs vers les bibliothèques cryptographiques dans System.Security.Cryptography, qui produisent des valeurs non seulement aléatoires mais également résistantes à la prédiction.

Conclusion : API simple, différences significatives

[9:30 - 9:50] Ce qui rend ce sujet trompeur, c'est à quel point il faut peu de code. Une seule ligne peut produire un nombre aléatoire par l'une de ces approches. La complexité ne réside pas dans la syntaxe, mais dans la compréhension des garanties que chaque méthode fournit : sécurité des threads, reproductibilité, ou force cryptographique.

Conclusion

[9:50 - 10:05] Pour récapituler : Random.Shared couvre la plupart des besoins sans configuration et avec une sécurité de threads intégrée. Les instances semées de Random vous permettent de reproduire une séquence spécifique lorsque le débogage ou les tests l'exigent. Les générateurs cryptographiques appartiennent au code sensible à la sécurité où la prévisibilité est une vulnérabilité, pas une fonctionnalité.

La prochaine fois que vous aurez besoin d'un nombre aléatoire en C#, la décision se résume à une question : devez-vous rejouer cette séquence plus tard ? Si la réponse est non, Random.Shared est tout ce dont vous avez besoin.

Astuce Exemple : Lorsque vous appelez Random.Shared.Next(min, max), notez que max est exclusif. Une plage de 1 à 100 nécessite de passer 1 et 101. Cette limite de hors d'un s'applique également aux instances avec graine.

Regardez la vidéo complète sur sa chaîne YouTube et obtenez plus d'aperçus sur les fondamentaux de C#.

Hero Worlddot related to Générer des nombres aléatoires en C#
Hero Affiliate related to Générer des nombres aléatoires en 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