Generación de números aleatorios en C#
[[academy-video-youtube({"vid": "GFLPncbEi-w", "start_time": "0", "title": "Generando Números Aleatorios en C#", "creator": "Tim Corey", "length": "10m 05s"})]]
Generar números aleatorios en C# parece que debería ser una instrucción de una sola línea, y en muchos casos lo es. Pero el lenguaje ofrece más de una manera de producir valores aleatorios, y las diferencias entre ellos importan una vez que se toma en cuenta la seguridad de hilos, la reproducibilidad y el caso de uso. Elegir el enfoque incorrecto puede introducir errores sutiles en el código multihilo o hacer que un defecto reportado sea imposible de reproducir.
En su video "Generando números aleatorios en C#", Tim Corey explica la clase clásica Random, explica por qué existen los valores de semilla y presenta Random.Shared como el valor predeterminado moderno. Cubriremos cada enfoque junto con el razonamiento detrás de él, para que pueda elegir el correcto sin adivinanzas. Si alguna vez se ha preguntado por qué hay múltiples caminos para algo que suena tan simple, este artículo lo desglosa.
Configurando la Demostración
[0:11 - 0:59] Tim trabaja dentro de una aplicación de consola en ejecución en Visual Studio 2026 (actualmente en vista previa) con .NET 10. Indica que todo lo demostrado aquí se aplica igualmente a .NET 9 y Visual Studio 2022, para que pueda seguir con las herramientas que ya tenga instaladas.
La disposición de demostración es un bucle for que imprime dos valores aleatorios uno al lado del otro en cada iteración. Tener dos salidas ejecutándose en paralelo facilita observar si ambos generadores se comportan de manera independiente o producen resultados coincidentes, una distinción que se vuelve importante una vez que los valores de semilla entran en escena.
La Clase Clásica Random
[0:59 - 3:28] El enfoque original para generar enteros aleatorios en C# implica crear una instancia de la clase Random:
Random rng1 = new Random();
Random rng2 = new Random();Random rng1 = new Random();
Random rng2 = new Random();Cada instancia mantiene su propio estado interno. Llamar a .Next(1, 101) en cualquiera de ellos produce un entero entre 1 y 100. Tim destaca un detalle que desconcierta a los recién llegados: el valor mínimo es inclusivo, pero el máximo es exclusivo. Si deseas valores del 1 al 100, debes pasar 1 y 101, no 1 y 100.
int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);int output1 = rng1.Next(1, 101);
int output2 = rng2.Next(1, 101);Ejecutar la aplicación confirma que ambas instancias producen diferentes secuencias. Ese resultado se siente intuitivo, pero lo que sucede cuando ambas instancias comparten el mismo punto de partida cuenta una historia diferente.
Una advertencia importante sobre este enfoque: las instancias individuales de Random no son seguras para hilos. Si su aplicación realiza procesamiento paralelo y múltiples hilos acceden a la misma instancia, el estado interno puede dañarse, produciendo ceros o valores repetidos. La práctica segura es crear una instancia por hilo. Esa restricción es una de las razones por las que el lenguaje introdujo más tarde una mejor alternativa.
Valores de Semilla y Secuencias Reproducibles
[3:28 - 6:00] Tim luego pasa una semilla explícita a ambos constructores:
Random rng1 = new Random(25);
Random rng2 = new Random(25);Random rng1 = new Random(25);
Random rng2 = new Random(25);La salida cambia dramáticamente. Ambos generadores ahora producen secuencias idénticas: 79, 16, 25, 90, 50, 41, y así sucesivamente. Los números aún son individualmente impredecibles si no conoce la semilla, pero dado el mismo valor inicial, la progresión es determinista.
¿Por qué alguien querría eso? Tim da un ejemplo práctico. Imagine un juego que genera eventos aleatorios a lo largo de una sesión. Un jugador informa un error, pero reproducirlo parece imposible porque los resultados fueron aleatorizados. Si el juego registra la semilla utilizada para esa sesión, un desarrollador puede recrear la cadena exacta de decisiones inicializando una nueva instancia de Random con el mismo valor. La misma lógica se aplica a escenarios de pruebas unitarias donde necesita salidas consistentes para escribir afirmaciones confiables contra un comportamiento aleatorio.
Las instancias con semilla le dan aleatoriedad controlada: una secuencia que parece impredecible pero que se puede reproducir bajo demanda. Esa capacidad es la razón por la cual el constructor clásico Random que acepta una semilla no ha sido declarado obsoleto, aunque ahora existe una API más sencilla.
Random.Shared: El Valor Predeterminado Moderno
[7:36 - 9:01] A partir de .NET 6, el enfoque recomendado para la mayoría de la generación de números aleatorios es Random.Shared:
int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);int output1 = Random.Shared.Next(1, 101);
int output2 = Random.Shared.Next(1, 101);No hay ninguna instancia involucrada. Random.Shared es una instancia estática y segura para hilos gestionada por el entorno de ejecución. Llamas a .Next() (o cualquiera de los otros métodos en la clase Random) y recibes un valor sin preocuparte por la duración del objeto o la concurrencia.
Tim ejecuta la demostración dos veces para demostrar el punto. La primera ejecución comienza con 94 y 91; el segundo comienza con 42 y 70. A diferencia de una instancia con semilla, Random.Shared parte de un estado inicial diferente cada vez que se inicia el proceso. No puede establecer una semilla, lo que significa que no puede producir una secuencia reproducible a través de esta API. Ese es el intercambio: simplicidad y seguridad a cambio de renunciar a la reproducción determinística.
Más allá de .Next(), Random.Shared expone métodos para generar dobles, llenar arreglos de bytes y mezclar colecciones. Para la gran mayoría del código de aplicación donde necesita un valor aleatorio rápido sin ceremonia, esta única propiedad estática reemplaza el trabajo preliminar de manejar sus propias instancias.
Escogiendo el Enfoque Correcto
[9:01 - 9:30] Tim cierra con un marco de decisión conciso. Para aleatoriedad diaria (elegir un valor, mezclar una lista, seleccionar un elemento aleatorio), Random.Shared es la elección adecuada. No requiere configuración, maneja la concurrencia y se comporta correctamente a través de hilos.
Cuando necesites una serie de salidas repetible, ya sea para depuración, pruebas o simulación, crea una instancia dedicada de Random con una semilla conocida. Tenga en cuenta que esas instancias no son seguras para compartir entre hilos.
Y para cualquier cosa que involucre seguridad (tokens, claves, sales de contraseñas), ninguno de los enfoques es apropiado. Tim dirige a los espectadores a las bibliotecas criptográficas en System.Security.Cryptography, que producen valores que no solo son aleatorios, sino también resistentes a la predicción.
Conclusión: API Simple, Diferencias Significativas
[9:30 - 9:50] Lo que hace que este tema sea engañoso es lo poco de código que se necesita. Una sola línea puede producir un número aleatorio a través de cualquiera de estos enfoques. La complejidad no está en la sintaxis sino en comprender qué garantías proporciona cada método: seguridad de hilos, reproducibilidad o fortaleza criptográfica.
Conclusión
[9:50 - 10:05] En resumen: Random.Shared cubre la mayoría de las necesidades con cero configuración y seguridad de hilo incorporada. Las instancias de Random con semilla te permiten reproducir una secuencia específica cuando la depuración o las pruebas lo requieren. Los generadores criptográficos pertenecen al código sensible a la seguridad donde la previsibilidad es una vulnerabilidad, no una característica.
La próxima vez que busque un número aleatorio en C#, la decisión se reduce a una pregunta: ¿necesita reproducir esta secuencia más tarde? Si la respuesta es no, Random.Shared es todo lo que necesitas.
Consejo de ejemplo: Al llamar a Random.Shared.Next(min, max), ten en cuenta que max es exclusivo. Un rango de 1 a 100 requiere pasar 1 y 101. Este límite de uno por uno también se aplica a las instancias con semilla.
Vea el video completo en su Canal de YouTube y obtenga más información sobre los fundamentos de C#.

