C# 提供多種隨機數產生方式,從基礎的 System.Random 到密碼學安全的 RandomNumberGenerator,再到 .NET 6+ 的 Random.Shared 與 RandomNumberGenerator.GetBytes 靜態方法。本文系統整理各類 API 適用場景、執行緒安全性、效能基準、常見陷阱與最佳實踐。
| 類別/方法 | 命名空間 | 演算法 | 密碼學安全 | 執行緒安全 | 適用場景 |
|---|---|---|---|---|---|
Random | System | 改良 Knuth 減法 (x86) / xoshiro256** (ARM/.NET 6+) | ❌ 可預測 | ❌ 實例不安全 | 遊戲、模擬、測試資料、非安全負載均衡 |
Random.Shared (.NET 6+) | System | 同 Random | ❌ | ✅ 內部鎖定 | 高併發非安全隨機需求 |
RandomNumberGenerator (RNG) | System.Security.Cryptography | OS CSPRNG (Windows: BCryptGenRandom, Linux: getrandom) | ✅ | ✅ 靜態方法安全 | 金鑰生成、Token、Nonce、Salt、密碼重設碼 |
RNGCryptoServiceProvider | System.Security.Cryptography | 同 RNG (舊 API) | ✅ | ✅ | 舊專案相容 (.NET Framework) |
// 單一實例重複使用 (避免種子相同)
private static readonly Random _random = new Random(); // .NET 6+ 種子來自 RNG
public int NextInt(int minInclusive, int maxExclusive) => _random.Next(minInclusive, maxExclusive);
public double NextDouble() => _random.NextDouble();
public void NextBytes(byte[] buffer) => _random.NextBytes(buffer);
// 亂序演算法 (Fisher-Yates)
public void Shuffle(T[] array)
{
for (int i = array.Length - 1; i > 0; i--)
{
int j = _random.Next(i + 1);
(array[i], array[j]) = (array[j], array[i]);
}
}
// 無需管理實例,執行緒安全
int dice = Random.Shared.Next(1, 7);
double prob = Random.Shared.NextDouble();
byte[] bytes = new byte[32];
Random.Shared.NextBytes(bytes);
using System.Security.Cryptography;
// .NET 6+ 靜態方法 (最簡潔)
byte[] key = RandomNumberGenerator.GetBytes(32); // 256-bit 金鑰
string token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32)); // URL-safe token
int secureInt = RandomNumberGenerator.GetInt32(100000, 999999); // 6 位數驗證碼
// 所有版本通用實例模式
using var rng = RandomNumberGenerator.Create();
byte[] salt = new byte[16];
rng.GetBytes(salt);
rng.GetNonZeroBytes(salt); // 確保無 0x00 (某些協定需求)
public static string GenerateSecurePassword(int length, bool includeSpecial = true)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const string special = "!@#$%^&*()-_=+[]{}|;:,.<>?";
string pool = chars + (includeSpecial ? special : "");
byte[] bytes = RandomNumberGenerator.GetBytes(length);
char[] result = new char[length];
for (int i = 0; i < length; i++)
result[i] = pool[bytes[i] % pool.Length];
return new string(result);
}
// 更簡潔的 Base64/URL-safe 字串
public static string GenerateToken(int byteLength = 32)
=> Convert.ToBase64String(RandomNumberGenerator.GetBytes(byteLength))
.Replace("+", "-").Replace("/", "_").TrimEnd('=');
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random _local;
private static readonly Random _global = new Random();
private static readonly object _lock = new();
public static Random Instance
{
get
{
if (_local == null)
{
int seed;
lock (_lock) seed = _global.Next();
_local = new Random(seed);
}
return _local;
}
}
public static int Next(int min, int max) => Instance.Next(min, max);
public static double NextDouble() => Instance.NextDouble();
}
// 固定種子 → 相同序列
var deterministic = new Random(12345);
var seq1 = Enumerable.Range(0, 10).Select(_ => deterministic.Next()).ToArray();
// 重新建立相同種子
var deterministic2 = new Random(12345);
var seq2 = Enumerable.Range(0, 10).Select(_ => deterministic2.Next()).ToArray();
Debug.Assert(seq1.SequenceEqual(seq2)); // 通過
public static double NextGaussian(Random rnd, double mean = 0, double stdDev = 1)
{
double u1 = 1 - rnd.NextDouble(); // (0,1]
double u2 = 1 - rnd.NextDouble();
double z = Math.Sqrt(-2 * Math.Log(u1)) * Math.Cos(2 * Math.PI * u2);
return mean + z * stdDev;
}
| 方法 | 耗時 | 每秒億次 | 備註 |
|---|---|---|---|
| Random.Next (單實例) | 1.02s | 0.98 | 基準 |
| Random.Shared.Next | 1.15s | 0.87 | 鎖定開銷 |
| ThreadLocal | 0.95s | 1.05 | 無鎖,最快 |
| RNG.GetInt32 | 4.8s | 0.21 | 系統呼叫開銷大 |
| RNG.GetBytes + BitConverter | 3.2s | 0.31 | 批次較快 |
Random.Shared、或 .NET 6+ 自動使用 RNG 種子。minInclusive, maxExclusive (同 `Random.Next`)。注意 maxExclusive 不包含。Random.SharedRandomNumberGeneratorC# 隨機數生態已完善:一般用途用 Random.Shared (.NET 6+) 或單一 Random 實例;安全用途一律用 RandomNumberGenerator 靜態方法。理解種子來源、執行緒安全、密碼學安全性邊界,即可避開 90% 的隨機數相關 Bug 與安全漏洞。