Альтернативный метод генерации UniqueId

В приложении, когда создаются особые типы объектов, мне нужно сгенерировать уникальный идентификатор для каждого из них. Объекты создаются на фабрике и имеют высокую вероятность создания «массовых» операций. Я понимаю, что «Случайный» из фреймворка в конце концов не такой уж и «случайный», поэтому я попытался добавить отметку времени следующим образом:

private string GenerateUniqueId()
{
    Random randomValue = new Random();
    return DateTime.Now.Ticks.ToString() + randomValue.Next().ToString();
}

К сожалению, даже это не работает. Для объектов, которые создаются в быстрой последовательности, я генерирую один и тот же уникальный идентификатор :-(

В настоящее время я реализую это примерно следующим образом:

private string GenerateUniqueId()
{
    Random randomValue = new Random();
    int value = randomValue.Next();
    Debug.WriteLine(value.ToString());
    Thread.Sleep(100);
    return DateTime.Now.Ticks.ToString() + value.ToString();
}

Поскольку это не очень большое приложение, я думаю, что будет достаточно простой и быстрой техники вместо реализации сложного алгоритма.

Пожалуйста, предложите.

Ответов (5)

Решение

GUID , вероятно , что вы ищете:

private string GenerateUniqueId()
{
    return Guid.NewGuid().ToString("N");
}

Если вам нужен меньший, более управляемый идентификатор, вы можете использовать что-то вроде этого:

private string GenerateUniqueId()
{
    using (var rng = new RNGCryptoServiceProvider())
    {
        // change the size of the array depending on your requirements
        var rndBytes = new byte[8];
        rng.GetBytes(rndBytes);
        return BitConverter.ToString(rndBytes).Replace("-", "");
    }
}

Примечание. Это даст вам только 64-битное число по сравнению с 128-битным идентификатором GUID, поэтому вероятность столкновения будет выше. Возможно, это не проблема в реальном мире. Если это проблема, вы можете увеличить размер byte массива, чтобы сгенерировать больший идентификатор.

Предполагая, что вам не нужен GUID, первый вариант будет статическим полем и заблокирован:

private static long lastId = 0
private static long GetNextId() {
  return Interlocked.Increment(ref lastId);
}

Если вы хотите что-то, основанное на временных тиках, запомните последнее значение и, если оно есть, вручную увеличьте и сохраните; в противном случае просто сохраните:

private static long lastTick = 0;
private static object idGenLock = new Object();
private static long GetNextId() {
  lock (idGenLock) {
    long tick = DateTime.UtcNow.Ticks;
    if (lastTick == tick) {
      tick = lastTick+1;
    }
    lastTick = tick;
    return tick;
  }
}

(Ни один из этих подходов не подходит для нескольких процессов.)

Почему ваша фабрика (предположительно однопоточная) не может генерировать последовательные уникальные целые числа? Если вы ожидали, что Random () сработает, почему не Guid () (или что-то подобное)?

В своих комментариях Codex вы говорите, что используйте уникальный идентификатор в качестве имени файла. Существует специальная функция для генерации криптографически безопасных имен файлов, Path.GetRandomFileName ()

Поскольку это криптографически безопасно, они будут уникальными даже в пакетных операциях. Формат немного ужасен, поскольку они оптимизированы для имен файлов, но он может работать и для других ссылок.

Если вы собираетесь прибегнуть к кодированию собственного генератора UUID, убедитесь, что вы солили генератор.

Я предлагаю вам ознакомиться с пакетом с открытым исходным кодом ossp-uuid , который представляет собой API-интерфейс ISO-C и интерфейс командной строки для генерации универсальных уникальных идентификаторов.