Generierung von Zufallszahlen in C#
Zufallszahlen in C# zu generieren scheint eine Einzeiler-Angelegenheit zu sein, und in vielen Fällen ist es das auch. Aber die Sprache bietet mehr als eine Möglichkeit, Zufallswerte zu erzeugen, und die Unterschiede zwischen ihnen sind wichtig, wenn Sie Thread-Sicherheit, Reproduzierbarkeit und Anwendungsfall berücksichtigen. Die falsche Herangehensweise kann subtile Fehler im Multi-Thread-Code einführen oder einen gemeldeten Fehler unmöglich reproduzierbar machen.
In seinem Video "Generating Random Numbers in C#" erklärt Tim Corey die klassische Random-Klasse, warum Seed-Werte existieren, und stellt Random.Shared als modernen Standard vor. Wir werden jeden Ansatz zusammen mit der Begründung dafür behandeln, damit Sie ohne Ratespiel den richtigen wählen können. Wenn Sie sich jemals gefragt haben, warum es mehrere Wege zu etwas so Einfachem gibt, erklärt dieser Artikel es.
Einrichten der Demo
[0:11 - 0:59] Tim arbeitet in einer Konsolenanwendung, die auf Visual Studio 2026 (derzeit in Vorschau) mit .NET 10 läuft. Er weist darauf hin, dass alles hier Demonstrierte auch for .NET 9 und Visual Studio 2022 gilt, sodass Sie mit dem fortfahren können, was auch immer Sie schon installiert haben.
Das Demo-Layout ist eine for-Schleife, die bei jeder Iteration zwei zufällige Werte nebeneinander ausgibt. Wenn zwei Ausgaben parallel laufen, ist es einfacher zu beobachten, ob sich beide Generatoren unabhängig verhalten oder übereinstimmende Ergebnisse liefern, eine Unterscheidung, die wichtig wird, sobald Samenwerte ins Spiel kommen.
Die klassische Random-Klasse
[0:59 - 3:28] Der ursprüngliche Ansatz zum Generieren von zufälligen Ganzzahlen in C# beinhaltet das Erstellen einer Instanz der Random-Klasse:
Random rng1 = new Random();
Random rng2 = new Random();
Random rng1 = new Random();
Random rng2 = new Random();
Jede Instanz verwaltet ihren eigenen internen Zustand. Ein Aufruf von .Next(1, 101) auf einer von beiden erzeugt eine Ganzzahl zwischen 1 und 100. Tim weist auf ein Detail hin, das Anfänger oft stolpern lässt: Der Mindestwert ist inklusive, aber der Höchstwert ist exklusiv. Wenn Sie Werte von 1 bis 100 wünschen, übergeben Sie 1 und 101, nicht 1 und 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);
Der Betrieb der Anwendung bestätigt, dass beide Instanzen unterschiedliche Sequenzen erzeugen. Dieses Ergebnis fühlt sich intuitiv an, aber was passiert, wenn beide Instanzen den gleichen Ausgangspunkt teilen, erzählt eine andere Geschichte.
Ein wichtiger Vorbehalt bei diesem Ansatz: Einzelne Random-Instanzen sind nicht thread-sicher. Wenn Ihre Anwendung parallele Verarbeitung durchführt und mehrere Threads auf dieselbe Instanz zugreifen, kann der interne Zustand beschädigt werden, was zu Nullen oder sich wiederholenden Werten führt. Die sichere Praxis ist es, für jeden Thread eine Instanz zu erstellen. Diese Einschränkung ist einer der Gründe, warum die Sprache später eine bessere Alternative eingeführt hat.
Samenwerte und reproduzierbare Sequenzen
[3:28 - 6:00] Tim übergibt dann einen expliziten Samen an beide Konstruktoren:
Random rng1 = new Random(25);
Random rng2 = new Random(25);
Random rng1 = new Random(25);
Random rng2 = new Random(25);
Die Ausgabe ändert sich dramatisch. Beide Generatoren produzieren jetzt identische Sequenzen: 79, 16, 25, 90, 50, 41 und so weiter. Die Zahlen sind immer noch individuell unvorhersehbar, wenn Sie den Samen nicht kennen, aber bei gleicher Ausgangswert ist die Abfolge deterministisch.
Warum sollte jemand das wollen? Tim gibt ein praktisches Beispiel. Stellen Sie sich ein Spiel vor, das während einer Sitzung zufällige Ereignisse generiert. Ein Spieler meldet einen Fehler, aber das Reproduzieren scheint unmöglich, weil die Ergebnisse zufällig waren. Wenn das Spiel den verwendeten Seed für diese Sitzung protokolliert, kann ein Entwickler die genaue Entscheidungskette rekonstruieren, indem er eine neue Random-Instanz mit demselben Wert initialisiert. Die gleiche Logik gilt für Unit-Tests-Szenarien, in denen Sie konstante Ausgaben benötigen, um zuverlässige Behauptungen gegen zufälliges Verhalten zu schreiben.
Instanzen mit Samen bieten Ihnen kontrolliertes Zufallsprinzip: eine Sequenz, die unvorhersehbar aussieht, aber bei Bedarf wieder abgespielt werden kann. Diese Fähigkeit ist der Grund, warum der klassische Random-Konstruktor, der einen Seed akzeptiert, nicht veraltet ist, obwohl es jetzt eine einfachere API gibt.
Random.Shared: Der moderne Standard
[7:36 - 9:01] Ab .NET 6 ist der empfohlene Ansatz für die meisten Zufallszahlengenerationen 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);
Es ist keine Instanziierung erforderlich. Random.Shared ist eine statische, thread-sichere Instanz, die vom Laufzeitsystem verwaltet wird. Sie rufen .Next() auf (oder eine der anderen Methoden in der Random-Klasse) und erhalten einen Wert, ohne sich um die Lebensdauer des Objekts oder die Parallelität kümmern zu müssen.
Tim führt die Demo zweimal aus, um den Punkt zu beweisen. Die erste Ausführung beginnt mit 94 und 91; der zweite beginnt mit 42 und 70. Anders als eine gesäte Instanz zieht Random.Shared jedes Mal, wenn der Prozess gestartet wird, aus einem anderen Anfangszustand. Sie können keinen Samen setzen, was bedeutet, dass Sie keine reproduzierbare Sequenz durch diese API erzeugen können. Das ist der Kompromiss: Einfachheit und Sicherheit im Austausch für den Verzicht auf deterministisches Abspielen.
Abgesehen von .Next() bietet Random.Shared Methoden zum Generieren von Double-Werten, Füllen von Byte-Arrays und Mischen von Sammlungen. Für die überwiegende Mehrheit des Anwendungscodes, bei dem Sie schnell einen zufälligen Wert benötigen, ersetzt diese einzige statische Eigenschaft das Boilerplate des Verwaltens eigener Instanzen.
Die richtige Herangehensweise wählen
[9:01 - 9:30] Tim schließt mit einem prägnanten Entscheidungsrahmen. Für alltägliche Zufälligkeit (z. B. das Auswählen eines Wertes, Mischen einer Liste, Auswahl eines zufälligen Elements) ist Random.Shared die richtige Wahl. Es erfordert keine Einrichtung, behandelt Parallelität und verhält sich korrekt über Threads hinweg.
Wenn Sie eine wiederholbare Serie von Ausgaben benötigen, sei es zum Debuggen, Testen oder zur Simulation, erstellen Sie eine dedizierte Random-Instanz mit einem bekannten Seed. Denken Sie daran, dass diese Instanzen nicht sicher über Threads hinweg geteilt werden können.
Und für alles, was Sicherheit betrifft (Tokens, Schlüssel, Passwortsalze), ist keine der beiden Ansätze geeignet. Tim weist die Zuschauer auf die kryptografischen Bibliotheken in System.Security.Cryptography hin, die nicht nur zufällige, sondern auch gegen Vorhersagen resistente Werte erzeugen.
Abschließend: Einfache API, bedeutungsvolle Unterschiede
[9:30 - 9:50] Was dieses Thema täuschend macht, ist, wie wenig Code es benötigt. Eine einzelne Zeile kann eine Zufallszahl durch einen dieser Ansätze erzeugen. Die Komplexität liegt nicht in der Syntax, sondern im Verständnis, welche Garantien jede Methode bietet: Threadsicherheit, Reproduzierbarkeit oder kryptografische Stärke.
Abschluss
[9:50 - 10:05] Zur Zusammenfassung: Random.Shared deckt die meisten Bedürfnisse mit null-Konfiguration und integrierter Thread-Sicherheit ab. Gesäte Random-Instanzen ermöglichen es Ihnen, eine spezifische Abfolge zu reproduzieren, wenn das Debuggen oder Testen dies erfordert. Kryptografische Generatoren gehören in sicherheitskritischen Code, bei dem Vorhersagbarkeit eine Schwachstelle ist, nicht ein Merkmal.
Das nächste Mal, wenn Sie in C# eine Zufallszahl benötigen, läuft die Entscheidung auf eine Frage hinaus: Müssen Sie diese Sequenz später wiederholen? Wenn die Antwort nein lautet, ist Random.Shared alles, was Sie brauchen.
Beispiel-Tipp: Beim Aufruf von Random.Shared.Next(min, max) beachten Sie, dass max exklusiv ist. Ein Bereich von 1 bis 100 erfordert die Übergabe von 1 und 101. Diese um eins versetzte Grenze gilt auch für gestützte Instanzen.
Sehen Sie sich das vollständige Video auf seinem YouTube Kanal an und gewinnen Sie weitere Einblicke in die C#-Grundlagen.
