2011-02-08 19 views
24

Podczas korzystania z klasy System.Random należy wykonać jej wystąpienie. Dlaczego nie jest to static? Ponieważ jeśli chcę losową liczbę od 0 do 9, mogę użyć statycznej metodę, System.Random.Next(int, int):Dlaczego klasa System.Random nie jest statyczna?

int ourRandomNumber = Random.Next(0,9); 

Więc dlaczego nie jest klasa tylko static?

Odpowiedz

31

Nie można używać różnych nasion, jeśli były statyczne - instancja Przypadkowa śledzi ten stan. Domyślnie Random używa bieżącego czasu jako materiału siewnego, ale ponowne użycie określonego materiału siewnego (tj. new Random(42)) pozwala dokładnie powtórzyć sekwencję liczb losowych - będą one zawsze takie same dla tego samego materiału siewnego. Ten aspekt jest bardzo ważny w niektórych aplikacjach. Na przykład Minecraft.

+1

W niektórych aplikacjach powtarzalność nie jest konieczna. Mogę wymyślić sytuacje, w których chciałbyś je ręcznie obsadzić, ale nigdy osobiście nie potrzebowałem go jawnie ustawić. – Davy8

+1

@ Davy8 - to prawda - w prawie wszystkich moich aplikacjach również nie potrzebuję seeda, ale "Random" nadal obejmuje tę funkcję. – BrokenGlass

+0

Mam program psów wyścigowych, które poruszają się losowo, a jeden wygrywa. Czy rozwiązaniem jest zrobienie tego samego nasienia? –

17

Random nie jest bezpieczny dla wątków. W każdym wątku może występować jedna instancja o wartości Random, ale jednocześnie nie należy używać jednej instancji z wielu wątków. Tak więc nie można mieć tylko jednej instancji Random w zmiennej statycznej i używać jej z metody statycznej.

Ponadto, sprawienie, że będzie statyczne, usunie możliwość nadania określonego materiału siewnego, jak wspomniano w artykule BrokenGlass.

Oczywiście nie byłoby zbyt trudno tworzyć statyczne metody, które zajmowałyby się bezpieczeństwem wątków, gdy nie trzeba potrzebować do określenia materiału siewnego, ale nadal pozostawić metody instancji, gdy chcesz użyć konkretna instancja. Osobiście uważam, że należy traktować "źródło liczb losowych" jako zależność, która ma zostać wstrzyknięta tam, gdzie jest to właściwe.

Mam numer article which covers some of this, który może okazać się przydatny.

+1

+1 dla bezpieczeństwa wątku. Czasami błędnie zakładam, że wbudowane klasy są bezpieczne dla wątków, o ile nie zaznaczono inaczej, jeśli w rzeczywistości nie są bezpieczne dla wątków, chyba że tak się stanie. – Davy8

+1

Twój artykuł jest bardzo miły, dziękuję –

4

Czasami chcesz "coś losowego", a nie zależy ci na tym, w jaki sposób osiąga się tę losową wartość. Posiadanie metody statycznej może zadziałać.

Czasem jednak chcesz mieć możliwość powtarzalnego otrzymywania tej samej losowej sekwencji. W tym celu użyjesz przeciążenia konstruktora, który pobiera wartość początkową, i w tym przypadku nie chcesz, aby jakikolwiek inny kod, który używa liczb losowych, zużył jeden z numerów z swojej sekwencji. W takim przypadku zdecydowanie potrzebujesz instancji klasy

2

Posiadanie powtarzalnej sekwencji "losowej" jest przydatne w testowaniu scenariuszy.

Można na przykład użyć go do testowania silnika gry w celu upewnienia się, że sztuczna inteligencja prawidłowo wybierała cele lub ścieżki - nawet jeśli ma losową ocenę ścieżki.

Oto bardzo uproszczony przykład. Bez względu na to, ile razy uruchomisz ten test, zawsze wybierze te same trzy karty, gdy otrzyma ten sam podstawowy generator liczb losowych. Może to być przydatne w celu zapewnienia, że ​​użyty generator liczb losowych to ten, który został dostarczony. Z jakiegoś powodu, gdyby nowy generator liczb losowych został wprowadzony bez zmiany testu, test zakończyłby się niepowodzeniem.

[TestMethod] 
public void TestRandomPicking() 
{ 
    Random random = new Random(1); 
    Deck deck = new Deck(random); 


    Assert.AreEqual(3, deck.PickCard().Value); 
    Assert.AreEqual(1, deck.PickCard().Value); 
    Assert.AreEqual(5, deck.PickCard().Value); 

} 

public class Deck 
{ 
    public Deck() 
    { 
     _randomizer = new Random(); 
    } 

    public Deck(Random randomizer) 
    { 
     _randomizer = randomizer; 
    } 

    Random _randomizer; 

    private List<Card> _cards = new List<Card> 
            { 
             new Card {Value = 1}, 
             new Card {Value = 2}, 
             new Card {Value = 3}, 
             new Card {Value = 4}, 
             new Card {Value = 5}, 
             new Card {Value = 6}, 
             new Card {Value = 7}, 
             new Card {Value = 8}, 
             new Card {Value = 9}, 
             new Card {Value = 10} 
            }; 

    private List<Card> Cards { get { return _cards; } } 

    public Card PickCard() 
    { 
     return Cards[_randomizer.Next(0, Cards.Count - 1)]; 
    } 
} 

public class Card 
{ 
    public int Value { get; set; } 
} 
1

Często, kiedy ktoś jest debugowanie programu, niewłaściwe zachowanie w jednym kroku może nie mieć widocznych objawów aż o wiele więcej kroków wykonany, przez który czas pierwotną przyczyną mogły być zasłonięte. W takich przypadkach może być bardzo użyteczne, aby móc ponownie uruchomić od podstaw program, który źle działał na przykład,krok 1.000.000 i uruchom pierwsze 99990 kroków dokładnie tak, jak za pierwszym razem, a następnie zatrzymaj się, aby programista mógł sprawdzić jego stan. Takie debugowanie nie będzie możliwe, jeśli program wygeneruje prawdziwie "losowe" liczby, ale będzie używał pseudolosowego generatora, który może zostać ponownie załadowany podczas drugiego uruchomienia z tym samym materiałem siewnym, co w pierwszym przebiegu.

Powiązane problemy