RandomKiedyś na blogu zrobiłem wpis o procedurze SQL Server, która zwraca losowe znaki.

Jak stworzyć podobny generator losowych znaków w C#?

Istnieje wiele możliwości.

Losowe znaki przy użyciu LINQ

Do utworzenia napisu z losowymi znakami naturalnie przydałaby się jakaś pętla “for”, ale od czego mamy LINQ w C#.

Oto metoda wykorzystująca metodę LINQ “Range”. Nie jest ona doskonała, ponieważ będzie ona zwracać tylko wielkie litery.

public static IEnumerable<char> BulidSequenceOfChars(int range)
{
    Random rnd = new Random();

    var chars = Enumerable.Range(0, range).Select(i => 
        ((char) ('A' + rnd.Next(0, 26))));

    return chars;
}

public static string BulidRandomString(int range)
{
    return new String(BulidSequenceOfChars(range).ToArray());
}

Dlaczego więc nie podać zbioru znaków, który nas interesuje do generowania losowego napisu.

public static string RandomString(int range)
{
    var chars = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var result = new string(
        Enumerable.Repeat(chars, range)
                    .Select(s => s[random.Next(s.Length)])
                    .ToArray());

    return result;
}

Obie metody są jednak wadliwe ze względu na klasę Random. Metody te rzeczywiście będą generować losowe znaki, ale na pewno nie będą one unikatowe.

Metody te też średnio się nadają, gdy mówimy o generowaniu haseł i tokenów. Do tych celów lepiej użyć  RNGCryptoServiceProvider-a.

Losowe znaki od RNGCryptoServiceProvider

Najprawdopodobniej najbardziej poprawne rozwiązanie, jeśli chodzi o generowanie losowych znaków.

public static string GetUniqueKey(int maxSize)
{
    char[] chars = new char[62];
    chars =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
    byte[] data = new byte[1];
    RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
    crypto.GetBytes(data);
    data = new byte[maxSize];
    crypto.GetBytes(data);
    StringBuilder result = new StringBuilder(maxSize);
    foreach (byte b in data)
    {
        result.Append(chars[b % (chars.Length)]);
    }
    return result.ToString();
}

Zdecydowanie bardziej zaufane rozwiązanie, jeśli mamy generować losowe hasła bądź sól do kryptowania danych.

Losowe znaki na bazie GUID

Unikatowość, a nie losowość.

Dlaczego nie użyć GUID, czyli unikatowego zestawu znaków stworzonego do tego celu. Musimy go tylko zamienić na napis.

public static string GuidString(int numOfCharsNeeded)
{
    if (numOfCharsNeeded <= 32)
        return Guid.NewGuid().ToString("n").Substring(0, numOfCharsNeeded);
    else
    {
        int f = numOfCharsNeeded / 32 + 1;
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i <= f; i++)
        {
            sb.Append(Guid.NewGuid().ToString("n"));
        }

        return sb.ToString().Remove(numOfCharsNeeded);
    }
}

Rozwiązanie to jednak też nie jest perfekcyjne i zapewne przy dużych napisach zawierających więcej niż 32 znaki bardzo powolne. Warto także pamiętać, że GUID składa się tylko z cyfry 0-9 i liter od A-F.