C#で 1〜10 をシャッフル表示 — “まず動かす” から “メソッド化・テスト” まで段階的に学ぶ

1. はじめに

プログラミング学習の初期は 「とにかく動くコードを書く → 後で整理する」 というサイクルが効果的です。本記事では 1〜10 の整数をシャッフルして表示する という小さな課題を使い、

  1. Main メソッドだけ で書く “動けば OK” 版
  2. 同じ処理を メソッド化 して読みやすくした版
  3. Fisher–Yates シャッフル に置き換えて精度・効率を上げる版
  4. NUnit + Assert.That で自動テストを書く版

と、少しずつリファクタリングを進める過程を体験します。


2. ステップ 1 — Main メソッドだけで動かす

using System;
using System.Linq;

namespace ShuffleSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 1〜10 を配列にする
            int[] numbers = Enumerable.Range(1, 10).ToArray();

            // 超ざっくりシャッフル(効率は二の次)
            Random rnd = new Random();
            for (int i = 0; i < numbers.Length; i++)
            {
                int j = rnd.Next(numbers.Length);   // 0〜9 の乱数
                int temp = numbers[i];
                numbers[i] = numbers[j];
                numbers[j] = temp;
            }

            // 結果を表示
            Console.WriteLine(string.Join(", ", numbers));
        }
    }
}
視点学び
流れプログラムは上から順に実行される
配列Enumerable.Range で連番の配列を簡単に作れる
乱数Random.Next(int max) は 0〜max-1 の整数を返す
デバッグConsole.WriteLine で動きを即確認できる

3. ステップ 2 — シャッフルをメソッド化

using System;
using System.Linq;

namespace ShuffleSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] numbers   = Enumerable.Range(1, 10).ToArray();
            int[] shuffled  = Shuffle(numbers);          // メソッド呼び出し
            Console.WriteLine(string.Join(", ", shuffled));
        }

        /// <summary>
        /// 配列を “とりあえず” シャッフルして新しい配列を返す
        /// </summary>
        static int[] Shuffle(int[] source)
        {
            int[] result = source.ToArray();             // 元配列を壊さない
            Random rnd   = new Random();

            for (int i = 0; i < result.Length; i++)
            {
                int j = rnd.Next(result.Length);
                (result[i], result[j]) = (result[j], result[i]);   // タプルで交換
            }
            return result;
        }
    }
}
メリット説明
再利用性Shuffle() を他の場面でも呼べる
可読性Main が “何をしているか” にフォーカスできる
保守性改善やバグ修正はメソッドだけ直せば良い

4. ステップ 3 — Fisher–Yates で “正しい” シャッフルに

static int[] ShuffleFY(int[] source)
{
    int[] result = source.ToArray();
    Random rnd   = new Random();

    for (int i = result.Length - 1; i > 0; i--)
    {
        int j = rnd.Next(i + 1);             // 0〜i の乱数
        (result[i], result[j]) = (result[j], result[i]);
    }
    return result;
}

Fisher–Yates シャッフル は「各並べ替え結果が等確率になる」ことが数学的に保証され、かつ O(n) と高速です。実務用途ではこちらを採用しましょう。


5. ステップ 4 — NUnit + Assert.That でテストを書く

5-1. テスト プロジェクトを用意

  • .NET CLI なら
dotnet new nunit -n ShuffleSample.Tests
dotnet add ShuffleSample.Tests reference ShuffleSample
  • Visual Studio なら「テストプロジェクトの追加」→「NUnit テストプロジェクト」を選択。

5-2. Constraint ベース構文(Assert.That)を使ったテスト

using NUnit.Framework;
using System.Linq;

namespace ShuffleSample.Tests
{
    public class ShuffleTests
    {
        [Test]
        public void ShuffleFY_UniquenessAndRange()
        {
            int[] source   = Enumerable.Range(1, 10).ToArray();
            int[] shuffled = Program.ShuffleFY(source);

            // 要素数は 10
            Assert.That(shuffled, Has.Exactly(10).Items);

            // 重複なし & 1〜10 と同等
            Assert.That(shuffled, Is.Unique.And.EquivalentTo(source));
        }
    }
}

なぜ Assert.That が推奨されるか

観点Assert.AreEqualAssert.That(Constraint 構文)
可読性expected / actual の順が混乱しがちactual → 条件 の順で自然
柔軟性Equal, True, Null… とメソッド乱立Is.*, Has.* を 組み合わせて表現
失敗メッセージシンプルで差分表示は限定的Constraint に応じて詳細・差分を表示
拡張性独自 Assert を足しにくい独自 Constraint を実装しやすい

実務・中規模以上のテストでは Assert.That のほうが保守性・表現力ともに優位です。


6. まとめ

  1. Main に全部書く → 動作を確認
  2. メソッド化 して読みやすさ・再利用性を高める
  3. アルゴリズムを改善(Fisher–Yates)して精度・性能を向上
  4. NUnit + Assert.That で自動テストを追加し、品質を継続的に守る

「まず動かす → 少しずつ整理 → テストを足す」という小さなリファクタリングの積み重ねが、後の拡張やバグ修正を格段に楽にしてくれます。まずは手を動かし、この 4 ステップを体験してみてください。

訪問数 8 回, 今日の訪問数 8回

C#,テスト

Posted by hidepon