new random() 在循环中会因毫秒级种子重复导致随机序列相同,应复用静态实例或使用 random.shared;next(min, max) 为左闭右开区间,密码级需用 randomnumbergenerator。

别用 new Random() 在循环里生成随机数——大概率得到一串重复值,不是 bug,是设计如此。
为什么 new Random() 在循环里会重复
Random 默认用系统时钟(毫秒级)做种子。循环执行极快,多次调用 new Random() 时拿到的种子几乎一样,于是整个随机序列完全相同。
- 现象:for 循环中连续 10 次
new Random().Next(1, 100),输出可能是42, 42, 42, ... - 根本原因:种子重复 → 伪随机序列重复
- 不是线程问题,单线程高频实例化也会触发
正确复用 Random 实例的方式
把 Random 实例声明为静态只读字段,确保整个 App 生命周期内只初始化一次。
- 普通场景(单线程或可控并发):
private static readonly Random _random = new Random(); - .NET 6+ 多线程安全首选:
Random.Shared.Next(1, 101)—— 无需手动同步,性能更好 - 不要在方法内部 new,也不要每次调用都 new
Next(min, max) 的边界容易搞错
Next(int minValue, int maxValue) 返回的是 [minValue, maxValue),即包含最小值、不包含最大值。
-
_random.Next(1, 10)→ 可能返回 1~9,永远不返回 10 - 要生成 1~10(含 10),得写
_random.Next(1, 11) -
NextDouble()固定返回 [0.0, 1.0),同样左闭右开
生成随机字符或混合字符串的常见写法
想生成类似 "a7xK2" 这种带字母数字的字符串,别硬拼 ASCII 码加判断大小写。
- 推荐字符池方式:
const string pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - 然后用
pool[_random.Next(pool.Length)]取单个字符 - 避免用
(char)_random.Next('a', 'z' + 1)这类写法——易出错且不支持大小写混合 - 如需密码级随机性(如 token),必须换
RandomNumberGenerator,Random不够安全
最常被忽略的一点:Random 是伪随机,且默认种子来源粗糙;哪怕你复用了实例,如果在极短时间内从多个线程/上下文同时首次调用它(比如 ASP.NET Core 中大量请求并发进入),仍可能因时钟分辨率不足导致初始序列趋同——这种边缘情况,Random.Shared 已内部处理,但自定义种子或旧版 .NET 仍需警惕。










