Przejdź do treści stopki
Iron Academy Logo
Naucz się C#
Naucz się C#

Inne Kategorie

Generowanie Losowych Liczb w C#

Tim Corey
10m 05s

Generowanie losowych liczb w C# wydaje się powinno być jednowierszowe, i w wielu przypadkach tak jest. Jednak język oferuje więcej niż jeden sposób na generowanie losowych wartości, a różnice między nimi mają znaczenie, gdy uwzględniasz bezpieczeństwo wątków, odtwarzalność i przypadek użycia. Wybranie niewłaściwego podejścia może wprowadzić subtelne błędy w kodzie wielowątkowym lub uczynić zgłoszony defekt niemożliwym do odtworzenia.

W swoim filmie "Generating Random Numbers in C#", Tim Corey omawia klasyczna Random klase, wyjasnia, dlaczego wartosci seed istnieją, i wprowadza Random.Shared jako nowoczesny domyslny wybor. Omówimy każde podejście wraz z uzasadnieniem za nim, abyś mógł wybrać odpowiednie, nie zgadując. Jeśli kiedykolwiek zastanawiałeś się, dlaczego istnieje tyle dróg do czegoś, co wydaje się tak proste, ten artykuł wyjaśnia to.

Konfigurowanie Dema

[0:11 - 0:59] Tim pracuje w aplikacji konsolowej działającej na Visual Studio 2026 (obecnie w wersji podglądowej) z .NET 10. Zaznacza, że wszystko, co tu demonstruje, obejmuje równie dobrze .NET 9 i Visual Studio 2022, więc możesz podążać za tym z dowolnymi narzędziami, które już masz zainstalowane.

Uklad demonstracyjny to for petla, która drukuje dwie losowe wartosci obok siebie przy kazdej iteracji. Posiadanie dwóch wyników wykonujących się równolegle ułatwia obserwację, czy oba generatory działają niezależnie, czy też zwracają zgodne wyniki, rozróżnienie, które nabiera znaczenia, gdy pojawiają się wartości nasion.

Klasyczna Klasa Random

[0:59 - 3:28] Oryginalne podejscie do generowania losowych liczb calkowitych w C# polega na utworzeniu instancji klasy Random:

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

Każda instancja utrzymuje swój własny wewnętrzny stan. Wywolanie .Next(1, 101) na dowolnej z nich zwraca liczba calkowita miedzy 1 a 100. Tim podkresla szczegól, który wprowadza nowicjuszy w blad: najmniejsza wartosc jest wlaczająca, ale maksymalna nie. Jesli chcesz wartosci od 1 do 100, przekaż 1 i 101, a nie 1 i 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);

Uruchomienie aplikacji potwierdza, że obie instancje produkują różne sekwencje. Ten wynik wydaje się intuicyjny, ale co się stanie, gdy obie instancje dzielą ten sam punkt wyjścia, mówi zupełnie inna historia.

Jedno wazne zastrzezenie dotyczace tego podejscia: indywidualne instancje Random nie sa bezpieczne wątkowo. Jeśli Twoja aplikacja wykonuje przetwarzanie równoległe i wiele wątków uzyskuje dostęp do tej samej instancji, wewnętrzny stan może zostać uszkodzony, generując zera lub powtarzające się wartości. Bezpieczną praktyką jest utworzenie jednej instancji na wątek. To ograniczenie jest jednym z powodów, dla których język wprowadził później lepszą alternatywę.

Wartości Nasion i Odtwarzalne Sekwencje

[3:28 - 6:00] Następnie Tim podaje jawne nasienie do obu konstruktorów:

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

Wynik zmienia się dramatycznie. Oba generatory teraz produkują identyczne sekwencje: 79, 16, 25, 90, 50, 41 i tak dalej. Liczby nadal są nieprzewidywalne indywidualnie, jeśli nie znasz nasienia, ale dany ten sam punkt wyjścia, progresja jest deterministyczna.

Czemu ktoś powinien tego chcieć? Tim daje praktyczny przykład. Wyobraź sobie grę, która generuje losowe zdarzenia w trakcie sesji. Gracz zgłasza błąd, ale odtworzenie go wydaje się niemożliwe, ponieważ wyniki były zrandomizowane. Jesli gra rejestruje seed uzyty w tej sesji, programista moze odtworzyc dokladny ciag decyzji, inicjalizując nową instancje Random z tą samą wartością. Ta sama logika dotyczy scenariuszy testów jednostkowych, gdzie potrzebujesz spójnych wyjść, aby napisać wiarygodne asercje przeciwko zrandomizowanemu zachowaniu.

Instancje z nasieniami dają kontrolowaną losowość: sekwencję wyglądającą nieprzewidywanie, ale mogącą być odtwarzaną na żądanie. Ta mozliwosc jest powodem, dlaczego klasyczny konstruktor Random akceptujący seed nie został wycofany, chociaz obecnie istnieje prostsze API.

Random.Shared: Nowoczesny Domyślny

[7:36 - 9:01] Począwszy od .NET 6, zalecane podejscie do wiekszosci generowania losowych liczb to 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);

Nie jest wymagane tworzenie instancji. Random.Shared to statyczna, bezpieczna wątkowo instancja zarzadzana przez runtime. Wywolujesz .Next() (lub jakakolwiek z innych metod klasy Random) i otrzymujesz wartosc, nie martwąc się o czas życia obiektu czy współbieżność.

Tim uruchamia demo dwa razy, aby to udowodnić. Pierwsze wykonanie zaczyna się od 94 i 91; druga zaczyna się od 42 i 70. W odroznieniu od instancji z seedem, Random.Shared korzysta z innego stanu początkowego za każdym razem, gdy proces jest uruchamiany. Nie możesz ustawić nasienia, co oznacza, że nie możesz wygenerować odtwarzalnej sekwencji za pomocą tego API. To jest kompromis: prostota i bezpieczeństwo w zamian za rezygnację z deterministycznego odtworzenia.

Poza .Next(), Random.Shared udostepnia metody generowania floatów, wypelniania tablic bajtów i mieszania kolekcji. Dla zdecydowanej większości kodu aplikacji, gdzie potrzebujesz szybkiej losowej wartości bez ceremonii, ta pojedyncza statyczna właściwość zastępuje złożoność zarządzania własnymi instancjami.

Wybór Właściwego Podejścia

[9:01 - 9:30] Tim kończy zwięzłym frameworkiem decyzyjnym. Dla codziennej losowosci (wybieranie wartosci, mieszanie listy, wybieranie losowego elementu), Random.Shared jest odpowiednim wyborem. Nie wymaga żadnej konfiguracji, obsługuje współbieżność i działa poprawnie między wątkami.

Kiedy potrzebujesz powtarzalnej serii wyników, czy to do debugowania, testów, czy symulacji, utwórz dedykowaną instancję Random z znanym seedem. Pamiętaj, że te instancje nie są bezpieczne do współdzielenia między wątkami.

A dla wszystkiego, co wymaga bezpieczeństwa (tokeny, klucze, sole hasła), żadne z tych podejść nie jest odpowiednie. Tim kieruje widzów do kryptograficznych bibliotek w System.Security.Cryptography, które produkują wartości nie tylko losowe, ale także odporne na przewidywanie.

Podsumowanie: Proste API, Znaczące Różnice

[9:30 - 9:50] To co czyni ten temat mylnym to jak mało kodu potrzeba. Jedna linia może wygenerować losową liczbę za pomocą któregokolwiek z tych podejść. Kompleksowość nie tkwi w składni, ale w zrozumieniu, jakie gwarancje dostarcza każda metoda: bezpieczeństwo wątków, odtwarzalność czy siła kryptograficzna.

Wnioski

[9:50 - 10:05] Podsumowując: Random.Shared pokrywa wiekszość potrzeb bez konfiguracji i z wbudowaną bezpiecznością wątkową. Instancje Random z seedem pozwalają odtworzyć konkretną sekwencję, kiedy wymaga tego debugowanie lub testowanie. Generator kryptograficzny należy do kodu wrażliwego na bezpieczeństwo, w którym przewidywalność jest wrażliwością, a nie cechą.

Następnym razem, gdy sięgniesz po losową liczbę w C#, decyzja sprowadza się do jednego pytania: czy musisz później odtworzyć tę sekwencję? Jeśli odpowiedź brzmi nie, Random.Shared to wszystko, czego potrzebujesz.

Przykład wskazówki: Przy wywoływaniu Random.Shared.Next(min, max), zanotuj, że max jest wykluczający. Zakres od 1 do 100 wymaga przekazania 1 i 101. Ta niedokładność jednego dotyczy także instancji z nasieniem.

Obejrzyj pełny film na jego Kanale YouTube i zdobądź więcej wiedzy na temat podstaw C#.

Hero Worlddot related to Generowanie Losowych Liczb w C#
Hero Affiliate related to Generowanie Losowych Liczb w C#

Zarabiaj więcej, dzieląc się tym, co kochasz

Tworzysz treści dla deweloperów pracujących z .NET, C#, Java, Python, czy Node.js? Zamień swoją wiedzę specjalistyczną na dodatkowy dochód!

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie