セルフレジで体験する「はじめてのクラス」チュートリアル

変数・条件分岐・ループ・配列まで学んだ人向け


0. ゴール

  1. クラスとは何かを体験して理解する
  2. カプセル化(データの隠蔽)の基本を感じ取る
  3. 既習内容(配列・if 文・for ループ)だけでミニアプリを完成させる

この章では行わないこと
コンストラクタ/継承/ポリモーフィズム/List/例外処理


1. 準備

  1. Visual Studio または VS Code で C# コンソールアプリを新規作成
  2. ソリューション名は、SelfCheckoutTutorialとしておきましょう

2. Step-by-Step 実装

Step 1 : 商品を表す Item クラスを作る

// Item.cs
class Item
{
    public string Code;   // 商品コード
    public string Name;   // 商品名
    public int    Price;  // 価格(円)
}

ポイント

  • “ひとまとめの箱” がクラス
  • 今回はフィールド(変数)だけを持つシンプル構成

Step 2 : レジ本体 SelfCheckout クラスを作る

// SelfCheckout.cs
using System;

class SelfCheckout
{
    private Item[] items = new Item[10];  // 買い物かご(10 点まで)
    private int index = 0;                // 今何点入っているか

    // スキャンしてカゴに入れる
    public void Scan(Item item)
    {
        items[index++] = item;
        Console.WriteLine($"{item.Name} を登録({item.Price}円)");
    }

    // 合計金額を計算
    public int CalcTotal()
    {
        int sum = 0;
        for (int i = 0; i < index; i++)
        {
            sum += items[i].Price;
        }

        return sum;
    }

    // レシート印字
    public void PrintReceipt()
    {
        Console.WriteLine("\n--- レシート ---");
        for (int i = 0; i < index; i++)
        {
            Console.WriteLine($"{items[i].Name,-10} {items[i].Price,6}円");
        }

        Console.WriteLine($"合計        {CalcTotal(),6}円");
        Console.WriteLine("------------\n");
    }
}

ポイント

  • items と index は private → 外から直接触れない
  • 外部はメソッド (Scan, CalcTotal, PrintReceipt) だけを呼べる

Step 3 : エントリポイント Program を書く

// Program.cs
using System;

class Program
{
    static void Main()
    {
        // 商品マスタ(object 初期化子で値を詰める)
        Item apple = new Item { Code="A01", Name="りんご", Price=120 };
        Item bread = new Item { Code="B01", Name="パン",   Price=150 };
        Item milk  = new Item { Code="C01", Name="牛乳",   Price=180 };

        var reg = new SelfCheckout();

        // りんご→パン→牛乳 をスキャン
        reg.Scan(apple);
        reg.Scan(bread);
        reg.Scan(milk);

        reg.PrintReceipt();

        // 支払(if 文だけで分岐)
        int total = reg.CalcTotal();
        Console.Write("支払方法 (1:現金 2:カード)> ");
        string sel = Console.ReadLine()!;

        if (sel == "1")
        {
            Console.Write("投入金額> ");
            int cash = int.Parse(Console.ReadLine()!);
            Console.WriteLine($"お預り: {cash}円  おつり: {cash - total}円");
        }
        else
        {
            Console.WriteLine($"カード決済 {total}円 承認OK");
        }

        Console.WriteLine("ご利用ありがとうございました");
    }
}

3. 動かしてみよう

  1. ビルドして実行
  2. コンソールに商品登録→レシート→支払選択が表示されれば成功

4. 学習チェック

チェック項目自分の理解
Item と SelfCheckout が別クラスである理由を説明できる□はい / □いいえ
private Item[] items を public にしたら何が起きるか想像できる□はい / □いいえ
Scan() を 11 回呼ぶとどうなるか確認し、原因を説明できる□はい / □いいえ

学習チェック ― サンプル解答

チェック項目模範的な説明例
Item と SelfCheckout が別クラスである理由責務分離 のため。
– Item は「商品」という“データの塊”を表し、価格や名前などの商品情報だけを持つ。
– SelfCheckout は「レジ」という“振る舞いの塊”を担当し、商品のスキャンや合計計算などの処理を行う。役割を分けることでコードが読みやすくなり、片方を変更してももう片方へ影響しにくくなる。
private Item[] items を public にした場合に起きること外部コードが items 配列へ直接アクセスできるようになり、以下の問題が起きやすい。
1. 不正操作: 配列要素を勝手に上書き/削除され合計金額が狂う。
2. 整合性破壊: index フィールドと配列の中身がズレ、CalcTotal() が正しい合計を出せなくなる。
3. バグ発見が困難: 不具合の原因がレジ内部か呼び出し側か区別しづらくなる。→ カプセル化の破壊。
Scan() を 11 回呼ぶとどうなるか、その原因配列 items の長さは 10 なので、11 回目で items[10] に代入しようとして IndexOutOfRangeException が発生し、プログラムが落ちる(例外未処理の場合)。原因は「固定長配列の上限を超えた」ため。index が 10 以上にならないようにチェックを入れるか、動的サイズの List<Item> に変更する必要がある。

5. 練習課題

  1. 商品を 5 点以上 用意してスキャンし、レシートを確認せよ
  2. SelfCheckout.CalcTotal() を 拡張税率 8% → 10% に切り替えられるよう改造せよ
  3. SelfCheckout に Clear() メソッド(配列を空に戻す)を追加して動作を確かめよ

以下は 練習課題 ①〜③ をすべて満たすサンプル実装です。

変更点の要所 → ★ コメントでマーキングしています。

using System;

namespace SelfCheckoutStep2
{
    // 1. 商品クラス(変更なし)
    class Item
    {
        public string Code;
        public string Name;
        public int    Price;          // 税抜き価格
    }

    // 2. レジ本体クラス
    class SelfCheckout
    {
        private Item[] items = new Item[20]; // ★ 最大 20 点に拡大
        private int index = 0;

        public int TaxPercent { get; set; } = 10;   // ★ 税率プロパティ(デフォルト 10%)

        // 商品を登録
        public void Scan(Item item)
        {
            items[index++] = item;
            Console.WriteLine($"{item.Name} を登録({item.Price}円)");
        }

        // ★ 合計(税込み)を計算
        public int CalcTotal()
        {
            int sum = 0;
            for (int i = 0; i < index; i++)
            {
                sum += items[i].Price;
            }
            double rate = 1 + TaxPercent / 100.0;    // 1.08 または 1.10
            return (int)Math.Round(sum * rate, MidpointRounding.AwayFromZero);
        }

        // レシート印字
        public void PrintReceipt()
        {
            Console.WriteLine("\n--- レシート ---");
            for (int i = 0; i < index; i++)
            {
                Console.WriteLine($"{items[i].Name,-12} {items[i].Price,6}円");
            }
            Console.WriteLine($"税率 {TaxPercent,2}%");
            Console.WriteLine($"合計        {CalcTotal(),6}円");
            Console.WriteLine("------------\n");
        }

        // ★ カゴを空にする
        public void Clear()
        {
            index = 0;                       // 先頭に戻すだけで OK
            Console.WriteLine("カゴを空にしました\n");
        }
    }

    // 3. 動作確認用プログラム
    class Program
    {
        static void Main()
        {
            // ① 商品を 5 点以上用意
            Item[] master =
            {
                new Item { Code="A01", Name="りんご",     Price=120 },
                new Item { Code="B01", Name="パン",       Price=150 },
                new Item { Code="C01", Name="牛乳",       Price=180 },
                new Item { Code="D01", Name="ミネラル水", Price=100 },
                new Item { Code="E01", Name="チョコ",     Price=220 }
            };

            var reg = new SelfCheckout();

            // --- ② 税率 8% でスキャン ---
            reg.TaxPercent = 8;                 // ★ 軽減税率に切り替え
            foreach (var item in master)        // 5 点すべて登録
            {
                reg.Scan(item);
            }
            reg.PrintReceipt();                 // レシート確認

            // --- ③ Clear() を呼び、税率 10% で再計算 ---
            reg.Clear();                        // ★ カゴの中身を空に
            reg.TaxPercent = 10;                // ★ 標準税率に戻す

            // 同じ商品をもう一度スキャン(今度は 10%)
            foreach (var item in master)
            {
                reg.Scan(item);
            }
            reg.PrintReceipt();
        }
    }
}

実行結果イメージ

りんご を登録(120円)
パン を登録(150円)
牛乳 を登録(180円)
ミネラル水 を登録(100円)
チョコ を登録(220円)

--- レシート ---
りんご           120円
パン             150円
牛乳             180円
ミネラル水       100円
チョコ           220円
税率  8%
合計             831円
------------

カゴを空にしました

りんご を登録(120円)
パン を登録(150円)
牛乳 を登録(180円)
ミネラル水 を登録(100円)
チョコ を登録(220円)

--- レシート ---
りんご           120円
パン             150円
牛乳             180円
ミネラル水       100円
チョコ           220円
税率 10%
合計             924円
------------

学習ポイントまとめ

課題反映されたコード重要キーワード
① 5 点以上スキャンforeach (var item in master)配列ループ
② 税率切替TaxPercent プロパティ & 計算式 rate = 1 + TaxPercent / 100.0プロパティ、四則演算
③ Clear()index = 0; で配列の先頭に戻すだけ状態リセット、カプセル化

このサンプルを写経 → 変更 → 実行しながら、「クラスの中で状態を管理し、それを操作するメソッドを追加する」感覚をつかんでください。


6. まとめと次の一歩

  • クラスは「データ(フィールド)+手続き(メソッド)」をまとめる道具
  • private でデータを隠し、メソッド経由で扱うのが カプセル化 の第一歩
  • 次回は コンストラクタ を使って安全に初期化する方法を学ぶ

これで 「クラスを使う」ことそのもの に慣れました。

自分のコードに “役割ごとに箱を作る” 視点を取り入れてみましょう。

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