2011-02-08 19 views
24

Quando si utilizza la classe System.Random, è necessario creare un'istanza. Perché non è static? Perché se voglio un numero casuale compreso tra 0 e 9, posso usare il metodo statico , System.Random.Next(int, int):Perché la classe System.Random non è statica?

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

Allora, perché non è la classe appena static?

risposta

31

Non sarebbe possibile utilizzare semi diversi se fosse statico: l'istanza Random tiene traccia di quello stato. Per impostazione predefinita, Casuale utilizza l'ora corrente come seme, ma riutilizzando un particolare seme (ad esempio new Random(42)) consente di ripetere esattamente la sequenza di numeri casuali, che saranno sempre gli stessi per lo stesso seme. Questo aspetto è molto importante in alcune applicazioni. Ad esempio, Minecraft.

+1

E in alcune app ripetibili non è necessario. Posso pensare a situazioni in cui vorresti seminare manualmente, ma non ho mai avuto la necessità di impostarne esplicitamente uno. – Davy8

+1

@ Davy8 - è vero - in quasi tutte le mie app non ho bisogno nemmeno di un seed, ma 'Random' copre ancora questa funzionalità. – BrokenGlass

+0

Ho un programma di Corse da Corsa, che si muovono in modo casuale e si vincono. È la soluzione per fare lo stesso seme ?? –

17

Random non è thread-safe. Va bene avere un'istanza di Random per thread, ma non si dovrebbe usare una istanza da più thread contemporaneamente. Quindi non puoi semplicemente avere un'istanza di Random in una variabile statica e usarla dal metodo statico.

Inoltre, il fatto di renderlo statico rimuoverebbe l'opportunità di fornire un seme specifico, come indicato da BrokenGlass.

Naturalmente, non sarebbe troppo difficile creare metodi statici, che si è occupata della sicurezza dei thread quando non si necessità per specificare un seme, ma ancora lasciare i metodi di istanza per quando si desidera utilizzare una particolare istanza. Personalmente trovo appropriato trattare "una fonte di numeri casuali" come una dipendenza da iniettare dove appropriato.

Ho un article which covers some of this e che potrebbe risultare utile.

+1

+1 per sicurezza thread. A volte capisco erroneamente che le classi incorporate siano sicure per i thread a meno che non siano contrassegnate diversamente quando non sono effettivamente thread-safe a meno che non si affermi di esserlo. – Davy8

+1

Il tuo articolo è molto carino, grazie –

4

A volte vuoi "qualcosa di casuale", e non ti importa di come sia arrivato quel valore casuale. Avere un metodo statico per quello potrebbe funzionare.

Tuttavia, a volte si desidera poter ottenere in modo ripetibile la stessa sequenza casuale. Per questo, si utilizza il sovraccarico del costruttore che assume un valore di inizializzazione e, in tal caso, non si desidera alcun altro codice che utilizza numeri casuali per consumare uno dei numeri da la sequenza. In tal caso, è assolutamente necessario un esempio della classe

2

Avere una sequenza "casuale" ripetibile è utile negli scenari di test.

Ad esempio, è possibile utilizzarlo per testare un motore di gioco per assicurarsi che un'IA abbia scelto correttamente bersagli o percorsi, anche se ha una valutazione casuale del percorso.

Ecco un esempio molto semplicistico. Indipendentemente dal numero di volte in cui esegui questo test, scegli sempre le stesse tre carte quando viene assegnato lo stesso generatore di numeri casuali di base. Ciò può essere utile per garantire che il generatore di numeri casuali utilizzato sia quello fornito. E, per qualche ragione, se un nuovo generatore di numeri casuali venisse introdotto senza alterare il test, il test fallirebbe.

[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

Spesso, quando uno è il debug di un programma, un comportamento improprio ad un passo non può avere sintomi visibili fino a quando sono eseguiti molti più passaggi, al termine del quale la causa originale potrebbe essere stato oscurato. In questi casi, può essere molto utile poter riavviare da zero un programma che non funziona correttamente ad es.Passo 1.000.000 e farlo eseguire il primo 999,990 o giù di lì esattamente come ha fatto la prima volta e quindi mettere in pausa per consentire al programmatore di esaminare il suo stato. Tale debug non sarà possibile se un programma genera numeri veramente "casuali", ma lo sarà se invece utilizza un generatore pseudo-casuale che può essere ricaricato nella seconda esecuzione con lo stesso seme usato nella prima esecuzione.

Problemi correlati