A classe System.Random gera números pseudoaleatórios: uma sequência determinística que parece aleatória, a partir de uma semente. Crie uma instância e chame os métodos dela:
Exemplo de saída:
1294270287
83
You rolled a 5
0.6060
Execute de novo e os números mudam. O limite superior é exclusivo: Next(1, 7) pode retornar 1, 2, 3, 4, 5 ou 6, nunca 7. Para um número entre 1 e 10, inclusive, escreva Next(1, 11). Passar um max menor que min lança uma ArgumentOutOfRangeException.
Números aleatórios em um intervalo
Next(min, max) cobre os inteiros. Para um double em um intervalo, escale NextDouble(); para um decimal como um preço, monte-o a partir de inteiros para que o resultado tenha a precisão que você quer:
Exemplo de saída:
23.6 C
72.42
heads
N
-3
rng.Next(100, 10000) / 100m sorteia um número inteiro de centavos e divide por um decimal, então o preço sempre tem exatamente duas casas decimais. Escalar um double daria valores como 72.4183.
Sementes: sequências reproduzíveis
Passar uma semente inteira torna a sequência repetível. Dois objetos Random com a mesma semente produzem exatamente os mesmos números, na mesma ordem:
Saída:
16 29 91 99 91
16 29 91 99 91
38
Sementes são o que você quer para testes, simulações que precisa executar de novo, replays em jogos e fases geradas proceduralmente ("semente do mundo 2026"). Registre a semente usada, e um resultado surpreendente pode ser reproduzido depois.
A Microsoft não promete que uma semente produza a mesma sequência em todas as versões do .NET. Não guarde a saída com semente como dado (por exemplo, gerar IDs de clientes a partir de uma semente fixa e esperar gerá-los de novo depois de uma atualização).
Sorteando um elemento
Um índice aleatório é Next(0, count), que é sempre um índice válido porque o limite superior é exclusivo:
Saída:
Winner 1 gets a sticker pack
Winner 2 gets a mug
Winner 3 gets a sticker pack
common 695
rare 260
legendary 45
O sorteio com pesos mapeia um número de 0 a 99 em faixas cujos tamanhos são os pesos: de 0 a 69 é comum, de 70 a 94 raro, de 95 a 99 lendário. Em 1.000 sorteios, as contagens ficam perto de 700, 250 e 50 (aqui 695, 260 e 45).
Embaralhando uma lista: Fisher-Yates
Para colocar uma lista em ordem aleatória, percorra a partir do fim e troque cada elemento com um elemento aleatório na mesma posição ou antes dela. Esse é o embaralhamento de Fisher-Yates; ele é rápido e toda ordem tem a mesma chance. Escrito como um método genérico, ele funciona com arrays e listas de qualquer tipo de elemento:
Saída:
5 A 8 6 4 7 2 3
4 2 1 3 5
Dois atalhos que você vai ver na internet são piores. list.OrderBy(x => rng.Next()) funciona, mas ordena, então é mais lento, e as chaves aleatórias podem colidir. Trocar com rng.Next(items.Count) em vez de rng.Next(i + 1) parece parecido, mas deixa algumas ordens mais prováveis que outras. No .NET 8 em diante, Random.Shared.Shuffle(array) faz um embaralhamento correto no próprio array, e rng.GetItems(choices, 5) sorteia cinco itens (com repetição).
A armadilha do new Random() dentro de um laço
Um bug clássico é criar um Random novo toda vez que um número é necessário:
// Don't do this
int RollDie()
{
var rng = new Random(); // new instance on every call
return rng.Next(1, 7);
}
No .NET Framework, new Random() sem semente usa o relógio do sistema (Environment.TickCount), que só muda a cada 10 a 16 milissegundos. Chamadas feitas em um laço rápido criam instâncias com a mesma semente e retornam o mesmo número, então um laço de rolagens de dado imprime 4 4 4 4 4. O .NET Core e o .NET 5+ dão a cada instância uma semente de uma fonte aleatória compartilhada, o que esconde o sintoma, mas criar um objeto por número continua sendo um desperdício, e o código quebra quando é reutilizado em projetos mais antigos ou na Unity.
A solução é uma instância para a classe, guardada em um campo:
Saída:
3 6 6 5 6 1 2 1
A semente aqui só existe para que o exemplo imprima a mesma coisa em toda execução; em um jogo real você escreveria new Random().
Threads e Random.Shared
Random não é thread safe. Se duas threads chamarem Next na mesma instância ao mesmo tempo, o estado interno dela pode se corromper; o sintoma conhecido é uma instância que passa a retornar 0 para sempre. As opções, da mais simples:
static class Dice
{
// .NET 6 and later: a thread-safe shared instance
public static int RollShared() => Random.Shared.Next(1, 7);
// Any version: one instance per thread
[ThreadStatic] private static Random _local;
private static Random Local => _local ?? (_local = new Random(Guid.NewGuid().GetHashCode()));
public static int RollPerThread() => Local.Next(1, 7);
// Any version: a lock around one shared instance
private static readonly Random _rng = new Random();
private static readonly object _sync = new object();
public static int RollLocked()
{
lock (_sync) { return _rng.Next(1, 7); }
}
}
Random.Shared é o padrão certo em código novo: é thread safe, não precisa de campo e recebe uma semente aleatória. Ele não aceita semente, então use o seu próprio new Random(seed) quando precisar de reprodutibilidade.
Números aleatórios criptograficamente seguros
System.Random é previsível por projeto. Quem conhece a semente, ou vê saídas suficientes, consegue calcular o resto. Isso serve para jogos e amostragem, e é errado para senhas, tokens de redefinição, IDs de sessão, chaves e sorteios. Para esses casos, use RandomNumberGenerator, de System.Security.Cryptography, que lê do gerador seguro do sistema operacional:
Exemplo de saída:
Verification code: 276062
Reset token: 391e40ab5fa205e6457e48661586d10a
Password: gyv8LWyajJcF
RandomNumberGenerator.GetInt32 (disponível no .NET Core 3.0 em diante) retorna um inteiro sem viés dentro do intervalo. Não monte códigos seguros com bytes[0] % 10: o resto da divisão deixa alguns dígitos mais prováveis que outros. No .NET 6 em diante, RandomNumberGenerator.GetBytes(16) retorna um array novo diretamente, e Convert.ToHexString(bytes) o formata. Para identificadores únicos em que o sigilo não importa, Guid.NewGuid() é mais simples.
Perguntas frequentes
Como gerar um número aleatório em C#?
Crie um objeto Random e chame Next: var rng = new Random(); int roll = rng.Next(1, 7); dá de 1 a 6. Next(max) dá de 0 a max - 1, e NextDouble() dá um double de 0.0 até 1.0, sem incluir o 1.0. No .NET 6 em diante, você pode dispensar o objeto e usar Random.Shared.Next(1, 7).
O limite superior de Random.Next é inclusivo?
Não. Next(min, max) retorna um número maior ou igual a min e estritamente menor que max. Para um número de 1 a 10, inclusive, escreva Next(1, 11). Isso faz de Next(0, list.Count) um índice válido de uma lista.
Por que new Random() dá os mesmos números?
No .NET Framework, new Random() recebe a semente do relógio do sistema, que só muda a cada alguns milissegundos, então várias instâncias criadas em um laço rápido recebem a mesma semente e a mesma sequência. O .NET Core e o .NET 5+ dão uma semente diferente a cada instância, mas a solução é a mesma em todo lugar: crie um Random e reutilize-o.
Como obter sempre os mesmos números aleatórios em C#?
Passe uma semente ao construtor: new Random(42). Duas instâncias com a mesma semente produzem a mesma sequência, o que torna reproduzíveis testes, simulações e geração procedural. A sequência de uma semente pode mudar entre versões do .NET, então não guarde a saída com semente como dado permanente.
System.Random é seguro o bastante para senhas ou tokens?
Não. Random é previsível: quem descobrir o estado ou a semente consegue reproduzir os números. Para senhas, tokens, chaves e qualquer coisa ligada a segurança, use System.Security.Cryptography.RandomNumberGenerator, por exemplo RandomNumberGenerator.GetInt32(0, 10) ou RandomNumberGenerator.GetBytes(32).