C#で 1〜10 をシャッフル表示― “まず動かす”→“メソッド化”→“正しいアルゴリズム”→“テストと VS 操作” まで一気に体験

0. この記事で学ぶ流れ

ステップゴールキーワード
1Main だけ でとにかく動かす乱数・配列
2メソッド化 して読みやすく再利用・副作用防止
3Fisher–Yates で正しいシャッフル等確率・O(n)
4NUnit + Assert.That で自動テストConstraint 構文
5Visual Studio でテストを実行Test Explorer

途中で出てくる ToArray() のコピー理由や「参照/参照先」の正しい言い回しも詳細に取り上げます。


1. ステップ 1 ― Main だけで“動くもの”

using System;
using System.Linq;

namespace ShuffleSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            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);
                int temp = numbers[i];
                numbers[i] = numbers[j];
                numbers[j] = temp;
            }

            Console.WriteLine(string.Join(", ", numbers));
        }
    }
}
  • まず動く:結果が出ることで安心感。
  • 乱数生成をループ外で 1 回、以外は効率を意識しない。

2. ステップ 2 ― シャッフルをメソッドに切り出す

using System;
using System.Linq;

namespace ShuffleSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] source   = Enumerable.Range(1, 10).ToArray();
            int[] shuffled = Shuffle(source);        // 呼び出すだけ
            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;
        }
    }
}

int[] result = source.ToArray(); が必要な理由

観点説明
参照型配列は参照型。メソッドには“参照値”が値渡しされるため、呼び出し元と同じ 参照先(= 同一配列オブジェクト) を共有する。
副作用コピーを取らずに並べ替えると、呼び出し元の配列まで変更されてしまう。
ToArray()LINQ 拡張。要素を同順で新しい int[] にコピーする。安全だが O(n) のコストとメモリ 2 倍が必要。
代替案var result = (int[])source.Clone(); や Array.Copy(…) でも可。目的は同じ。

3. ステップ 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;
}
  • すべての順列が等確率
  • ループは n-1 回で O(n)、高速かつ偏りなし。

4. ステップ 4 ― NUnit + Assert.That で品質を守る

4-1. テスト コード

using NUnit.Framework;
using System.Linq;

namespace ShuffleSample.Tests
{
    [TestFixture]
    public class ShuffleTests
    {
        [Test]
        public void ShuffleFY_UniquenessAndRange()
        {
            var src = Enumerable.Range(1, 10).ToArray();
            var dst = Program.ShuffleFY(src);

            Assert.That(dst, Has.Exactly(10).Items);
            Assert.That(dst, Is.Unique.And.EquivalentTo(src));
        }
    }
}

4-2. Assert.That が優れるポイント

AreEqual 系That 系 (Constraint)
expected/actual が逆転しがちactual → 条件 の自然な語順
メソッドが乱立Is, Has で 条件を組合せ
簡素な失敗メッセージ条件ごとに詳細な差分表示
拡張しづらい独自 Constraint が実装容易

5. ステップ 5 ― Visual Studio で NUnit テストを実行

  1. ソリューションを右クリック → [追加] > [新しいプロジェクト]
    • NUnit Test Project (.NET Core) を選択。
  2. テスト プロジェクト → [プロジェクト参照の追加] で本体プロジェクトにチェック。
  3. テスト クラスを作成し [TestFixture] / [Test] 属性を付ける。
  4. メニュー [テスト] > [Test Explorer] (Ctrl + E, T) を開く。
  5. Run All (Ctrl + R, A) で実行。
  6. 赤/緑アイコンで結果確認。右クリック [デバッグ] でブレークポイントも利用可。

ヒント: 歯車→ Run Tests After Build を ON にするとビルドごとに自動テスト。


6. まとめ ― 小さなリファクタリングを回す

  1. 動くものを最速で作り、振る舞いを確かめる。
  2. メソッド化 で読みやすさ・再利用性・副作用抑止。
  3. アルゴリズム改善 (Fisher-Yates) で正確さと性能を確保。
  4. テスト を書いて安全網を敷く。Assert.That で可読性◎。
  5. IDE 操作 を覚え、コード→テスト→デバッグをシームレスに。

この一連を体験すれば、「まず動かす」から「保守しやすいコード」へ成長するリファクタリングの筋肉が付きます。ぜひ手を動かし、赤 → 緑 のテストサイクルを回してみてください。

訪問数 30 回, 今日の訪問数 1回

C#,テスト,メソッド

Posted by hidepon