Avatar ·

Как сгенерировать случайное целое число в C#?

Привет всем! Я новичок в C# и пишу простую игру-угадайку. Мне нужно, чтобы программа загадывала случайное число от 1 до 100, а пользователь его угадывал. Но я столкнулся с проблемой: при каждом запуске программы выпадает одно и то же число. Вот мой код: ```csharp Random random = new Random(); int secretNumber = random.Next(1, 101); ``` Я создаю новый объект Random, но каждый раз получаю 34, потом 34, потом опять 34... Что я делаю не так? Я слышал, что можно использовать `Random.Next()` или `Random.NextDouble()`, но мне нужно именно целое число. Может быть, я неправильно задаю диапазон? Пожалуйста, объясните, как правильно генерировать случайные целые числа в C#. Буду благодарен за примеры кода и объяснение, почему мой код не работает. Заранее спасибо! <<>>

Проблема с генерацией случайных чисел в C#

Вы столкнулись с классической ошибкой начинающих разработчиков. Проблема в том, что вы создаёте новый экземпляр Random каждый раз, когда нужно получить случайное число. Random использует системное время как начальное зерно (seed). Если вы создаёте несколько объектов Random за короткий промежуток времени, они получают одинаковое зерно и генерируют одинаковую последовательность чисел.

Правильное решение: один экземпляр Random

Создайте один статический экземпляр Random и используйте его повторно. Вот правильный подход:

class Program
{
    private static readonly Random random = new Random();
    
    static void Main()
    {
        for (int i = 0; i < 5; i++)
        {
            int secretNumber = random.Next(1, 101);
            Console.WriteLine($"Попытка {i + 1}: {secretNumber}");
        }
    }
}

Основные методы Random для целых чисел

  • Random.Next() — возвращает неотрицательное случайное целое число (0 до int.MaxValue - 1).
  • Random.Next(int maxValue) — возвращает число от 0 (включительно) до maxValue (исключительно). Пример: random.Next(10) даёт числа 0-9.
  • Random.Next(int minValue, int maxValue) — возвращает число от minValue (включительно) до maxValue (исключительно). Пример: random.Next(1, 101) даёт числа 1-100.

Безопасность для многопоточности

Если ваше приложение использует несколько потоков, статический Random может вызвать проблемы (он не потокобезопасен). Используйте ThreadLocal<Random> или блокировку:

private static readonly ThreadLocal<Random> threadRandom = 
    new ThreadLocal<Random>(() => new Random(Environment.TickCount));

public static int GenerateRandomInt(int min, int max)
{
    return threadRandom.Value.Next(min, max);
}

Криптостойкие случайные числа

Для паролей, токенов или других задач, требующих безопасности, используйте System.Security.Cryptography.RandomNumberGenerator:

using System.Security.Cryptography;

public static int GetSecureRandomInt(int min, int max)
{
    using (var rng = RandomNumberGenerator.Create())
    {
        byte[] bytes = new byte[4];
        rng.GetBytes(bytes);
        uint value = BitConverter.ToUInt32(bytes, 0);
        return (int)(min + (value % (max - min)));
    }
}

Почему ваш код не работал?

Если вы создавали Random random = new Random(); внутри цикла или при каждом вызове метода, все экземпляры получали одно и то же зерно (временную метку с одинаковым значением). Поэтому числа повторялись. Запомните правило: один объект Random на всё приложение (или на поток).

Полный пример для вашей игры

class GuessGame
{
    private static readonly Random random = new Random();
    
    static void Main()
    {
        int secretNumber = random.Next(1, 101);
        Console.WriteLine("Угадай число от 1 до 100!");
        
        while (true)
        {
            string input = Console.ReadLine();
            if (int.TryParse(input, out int guess))
            {
                if (guess == secretNumber)
                {
                    Console.WriteLine("Правильно!");
                    break;
                }
                Console.WriteLine(guess < secretNumber ? "Больше!" : "Меньше!");
            }
            else
            {
                Console.WriteLine("Введите целое число!");
            }
        }
    }
}
<<>>

Альтернативный подход: использование Random.Shared

Начиная с .NET 6, появился статический экземпляр Random.Shared, который является потокобезопасным. Это самый простой способ получить случайное число без создания собственного экземпляра:

int secretNumber = Random.Shared.Next(1, 101);

Random.Shared автоматически управляет потоками и не требует ручного создания объектов. Он идеально подходит для большинства задач, включая игры и симуляции.

Когда использовать Random.Shared?

  • В однопоточных приложениях — как замена обычному Random.
  • В многопоточных приложениях — без дополнительных блокировок.
  • Когда нужна простота и минимализм кода.

Однако для криптографических задач всё равно используйте RandomNumberGenerator, так как Random.Shared не является криптостойким.

Пример с Random.Shared

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(Random.Shared.Next(1, 101));
}

Этот подход избавляет вас от необходимости объявлять статическое поле Random. Просто используйте Random.Shared в любом месте программы. Обратите внимание: если ваша среда — .NET Framework (до .NET Core 3.0), то Random.Shared недоступен, и придётся использовать классический способ с одним экземпляром.