C#を用いてアプリケーションを開発する際、ユーザーIDの採番、パスワードの一時発行、認証用トークンの生成、あるいはテストデータの作成など、「ランダムな文字列を生成する」という処理は非常に頻繁に求められます。

しかし、一口にランダムな文字列といっても、単にランダムであれば良い場合もあれば、暗号学的な強固さが求められる場合、あるいは数百万件の生成に耐えうる実行速度が求められる場合など、その用途は多岐にわたります。

本記事では、C#におけるランダム文字列生成の標準的な実装パターンから、最新の .NET 環境(.NET 6/7/8/9以降)で推奨される高効率な手法、さらにはセキュリティを重視した手法まで、テクニカルな視点で徹底的に解説します。

用途に合わせて最適なコードを選択できるよう、具体的なソースコードと実行結果を交えてご紹介します。

ランダム文字列生成の基本概念と注意点

実装に入る前に、C#でランダムな値を扱う際の基本的な考え方を整理しておきましょう。

C#には主に2つの乱数生成メカニズムが存在します。

まず一つは、System.Random クラスです。

これは「疑似乱数ジェネレータ」と呼ばれ、計算によって乱数に見える数列を生成します。

高速に動作するため一般的な用途には適していますが、セキュリティ上の重要度が高い用途(パスワード生成など)には適していません

また、マルチスレッド環境での安全性(スレッドセーフ)にも注意が必要です。

もう一つは、System.Security.Cryptography.RandomNumberGenerator です。

こちらは「暗号学的に強い乱数」を生成します。

予測不可能性が極めて高く、セキュリティトークンやパスワードの生成に必須となりますが、計算コストは System.Random よりも高くなります。

これらの特徴を理解した上で、具体的な実装パターンを見ていきましょう。

System.Random を使用した標準的な実装

もっとも一般的で、古くから使われている手法です。

文字の候補となる配列や文字列を定義し、そこからランダムにインデックスを選択して文字列を組み立てます。

基本的な実装コード

以下のコードは、指定された長さの英数字からなるランダム文字列を生成するクラスです。

C#
using System;
using System.Linq;
using System.Text;

public class RandomStringBasic
{
    // 文字の候補(英数字)
    private const string Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    /// <summary>
    /// 指定された長さのランダムな文字列を生成します
    /// </summary>
    /// <param name="length">生成する文字列の長さ</param>
    /// <returns>ランダムな文字列</returns>
    public static string Generate(int length)
    {
        // 乱数生成器のインスタンス化
        // 注意:ループ内でインスタンス化すると同じ値が生成されるリスクがある
        Random random = new Random();
        
        StringBuilder result = new StringBuilder(length);
        for (int i = 0; i < length; i++)
        {
            // Charactersの中からランダムに1文字選択
            int index = random.Next(Characters.Length);
            result.Append(Characters[index]);
        }

        return result.ToString();
    }
}

// 実行例
public class Program
{
    public static void Main()
    {
        string randomString = RandomStringBasic.Generate(12);
        Console.WriteLine($"Generated String: {randomString}");
    }
}
実行結果
Generated String: aB3kL9pQ2mRt

この手法の課題と解決策

上記のコードには、実務運用においていくつかの懸念点があります。

  1. シード値の問題new Random() を頻繁に呼び出すと、内部のシード値(時刻ベース)が同じになり、全く同じ文字列が生成される ことがあります。
  2. スレッドセーフではない:複数のスレッドから同時に同じ Random インスタンスにアクセスすると、内部状態が壊れる可能性があります。

これらの問題を解決するために、モダンな C#(.NET 6以降)では Random.Shared プロパティの使用が推奨されています。

Random.Shared を用いたモダンな実装

.NET 6で導入された Random.Shared は、スレッドセーフであり、パフォーマンスも最適化されています。

開発者が自分でインスタンスを管理する必要がなく、最も手軽に利用できる選択肢です。

LINQを活用した簡潔な記述

LINQを使用すると、ループ処理を記述せずに宣言的に実装することができます。

C#
using System;
using System.Linq;

public class ModernRandomString
{
    private const string CharPool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    public static string Generate(int length)
    {
        // Random.Shared を使用することでスレッドセーフかつ効率的に生成
        return new string(Enumerable.Repeat(CharPool, length)
            .Select(s => s[Random.Shared.Next(s.Length)]).ToArray());
    }
}

// 実行例
Console.WriteLine(ModernRandomString.Generate(16));
実行結果
X7G2P9L1QW4V5B8N

このコードは非常に短く読みやすいですが、メモリ効率(アロケーション)の観点では、大量の生成を行う場合には改善の余地があります。

大量の文字列を生成するバッチ処理などでは、後述する Span<T> を利用した手法を検討してください。

セキュリティを重視した RandomNumberGenerator の活用

パスワードリセット用のトークンや API キーなど、推測されては困るランダム文字列を生成する場合は、必ず System.Security.Cryptography.RandomNumberGenerator を使用してください。

GetItems メソッド(.NET 8以降)による効率化

.NET 8からは、RandomNumberGeneratorGetItems メソッドが追加されました。

これにより、従来は複雑だった「安全なランダム抽出」が劇的に簡単になりました。

C#
using System;
using System.Security.Cryptography;

public class SecureRandomString
{
    private static readonly char[] Characters = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*".ToCharArray();

    public static string GenerateSecure(int length)
    {
        // RandomNumberGenerator.GetItems を使用して安全かつ簡単に抽出
        // .NET 8 以降で利用可能
        char[] result = RandomNumberGenerator.GetItems(Characters, length);
        return new string(result);
    }
}

// 実行例
string secureToken = SecureRandomString.GenerateSecure(32);
Console.WriteLine($"Secure Token: {secureToken}");
実行結果
Secure Token: zP9!fG2#kL8*qA1@mN5$xR7&jB3%vD6^

この手法は、暗号学的な安全性を確保しつつ、記述の簡潔さも両立しています。

新規開発においては、特別な理由がない限りこの方法を第一候補にすべきです。

パフォーマンスを極限まで高める(Span と String.Create)

数万件、数百万件のランダム文字列を高速に生成する必要がある場合、ガベージコレクション(GC)の負荷を減らすことが重要です。

C#の string.CreateSpan<char> を組み合わせることで、中間バッファの作成を抑え、高速な生成が可能になります。

高パフォーマンスな実装例

C#
using System;
using System.Security.Cryptography;

public class HighPerformanceRandom
{
    private const string CharPool = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    public static string Generate(int length)
    {
        // string.Create を使用して、文字列メモリに直接書き込む
        return string.Create(length, CharPool, (span, pool) =>
        {
            // RandomNumberGenerator を使用しつつ、Spanに対して直接書き込み
            RandomNumberGenerator.Fill(System.Runtime.InteropServices.MemoryMarshal.AsBytes(span));

            for (int i = 0; i < span.Length; i++)
            {
                // バイト値をインデックスに変換(簡略化のため剰余を使用)
                // ※厳密な一様分布が必要な場合は補正処理が必要
                span[i] = pool[span[i] % pool.Length];
            }
        });
    }
}

この方法は、余計なメモリコピーが発生しないため、非常に高速です。

ただし、剰余計算(%)による偏りが微細に発生するため、厳密な統計的等価性が求められる場合には注意が必要です。

GUID を利用した一意性の確保

「ランダム」かつ「重複しないこと」が最優先であれば、Guid(Globally Unique Identifier)を使用するのが最も確実です。

C#
// GUIDからランダムな文字列を生成(ハイフン除去)
string guidString = Guid.NewGuid().ToString("n");
Console.WriteLine($"GUID String: {guidString}");
実行結果
GUID String: f47ac10b58cc4372a5670e02b2c3d479

GUIDは常に32文字(ハイフン除き)の16進数となるため、長さの指定や文字種(英大文字など)のカスタマイズには向きませんが、一意性の保証が必要なデータベースのキーなどには最適です。

実装パターンの比較表

用途に合わせて適切な方法を選択できるよう、各手法の特徴をまとめました。

手法セキュリティ速度推奨 .NET バージョン主な用途
Random.Shared.NET 6+一般的なUI表示、テストデータ
RNG.GetItems最高.NET 8+パスワード、セッショントークン
Guid.NewGuid全バージョンDB主キー、ファイル名の一意化
string.Create中〜高最高.NET Core 2.1+大量生成バッチ、高負荷サービス

実践的なユーティリティクラスの作成

最後に、これまでの知見を詰め込んだ、汎用性の高いランダム文字列生成ユーティリティの例を紹介します。

このクラス一つで、多くのユースケースに対応可能です。

C#
using System;
using System.Security.Cryptography;

public static class RandomStringGenerator
{
    private const string Alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private const string Numbers = "0123456789";

    /// <summary>
    /// 英数字混在のランダム文字列を生成(安全)
    /// </summary>
    public static string GetAlphanumeric(int length)
    {
        return Generate(length, Alphanumeric);
    }

    /// <summary>
    /// 数字のみのランダム文字列を生成(安全)
    /// </summary>
    public static string GetNumbers(int length)
    {
        return Generate(length, Numbers);
    }

    private static string Generate(int length, string charPool)
    {
        if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
        
        // .NET 8 以降の効率的な実装
        return new string(RandomNumberGenerator.GetItems(charPool.ToCharArray(), length));
    }
}

このユーティリティを使用することで、コードの可読性が向上し、チーム全体で安全な乱数生成手法を統一することができます。

まとめ

C#でランダムな文字列を生成する方法は、技術の進歩とともに進化してきました。

かつては System.Random のインスタンス管理に苦労していましたが、現代の C# では Random.SharedRandomNumberGenerator.GetItems といった非常に強力かつ簡潔な API が用意されています。

実装にあたっては、まず「その文字列にセキュリティが必要か?」を問いかけてください。

必要があるなら RandomNumberGenerator を、ないなら Random.Shared を選択するのが鉄則です。

また、.NET 8 以降を使用している環境であれば、パフォーマンスと安全性を両立した GetItems メソッドの積極的な活用を推奨します。

今回ご紹介したパターンを参考に、要件に応じた最適なランダム文字列生成処理を実装してみてください。