C# | 1 日 90 分 × 7 日アプリ学習:初級編

Web APP C#
スポンサーリンク

7日目のゴール

初級編 7日目のテーマは
「ここまで学んだ全部を“1つのアプリ”としてまとめること」 です。

入力
安全な数値変換(TryParse)
配列
メソッド分割
クラス(Student / StudyLog のような“意味のある型”)
クラス配列の検索・集計・フィルタ
メニューとメインループ

これらをバラバラの“技”としてではなく、
「1つの目的を持ったアプリ」として組み上げるのが、今日のゴールです。

今日は、あなた自身に直結する題材として
「学習記録アプリ」 を作ります。

C# の勉強時間を記録して、
あとから振り返れる、小さなコンソールアプリです。


アプリのイメージを言葉で固める

どんなことができるアプリにするか

まずは、コードを書く前に「仕様」を言葉で決めます。

今日作る「学習記録アプリ」は、こんなことができるイメージです。

コンソールで動く
メニューから操作を選ぶ
学習記録を追加できる
今までの記録を一覧表示できる
合計時間・平均時間・最長時間などの統計を見られる
特定の日付の記録を検索できる

ここまでできれば、
「自分の学習を C# で見える化するツール」として十分に実用的です。


学習記録を表すクラスを設計する

1件分の「学習記録」とは何か

まず、「1件分の学習記録」に何が必要かを考えます。

日付
科目(例: C#, JavaScript など)
勉強時間(分)
メモ(任意)

これを C# のクラスに落とし込みます。

class StudyRecord
{
    public string DateText;
    public string Subject;
    public int Minutes;
    public string Note;
}
C#

ここで大事なのは、
「現実の“1件分の記録”を、そのままクラスに写し取る」
という感覚です。

DateText は本当は DateTime 型にしてもいいのですが、
初級編では「文字列として扱う」で十分です。


アプリ全体の骨組みを作る

メインループとメニューの形を先に書く

次に、4日目でやった「メニュー付きアプリ」の形をベースに、
学習記録アプリの骨組みを書きます。

using System;

class Program
{
    static void Main(string[] args)
    {
        StudyRecord[] records = new StudyRecord[100];
        int recordCount = 0;

        while (true)
        {
            PrintMenu();
            int choice = ReadInt("番号を選んでください:");

            if (choice == 0)
            {
                Console.WriteLine("アプリを終了します。");
                break;
            }

            Console.WriteLine();

            switch (choice)
            {
                case 1:
                    AddRecord(records, ref recordCount);
                    break;

                case 2:
                    ShowAll(records, recordCount);
                    break;

                case 3:
                    ShowStats(records, recordCount);
                    break;

                case 4:
                    SearchByDate(records, recordCount);
                    break;

                default:
                    Console.WriteLine("0〜4 の範囲で選択してください。");
                    break;
            }

            Console.WriteLine();
        }
    }

    static void PrintMenu()
    {
        Console.WriteLine("===== 学習記録アプリ =====");
        Console.WriteLine("1: 学習記録を追加する");
        Console.WriteLine("2: 記録を一覧表示する");
        Console.WriteLine("3: 統計を見る");
        Console.WriteLine("4: 日付で検索する");
        Console.WriteLine("0: 終了");
    }

    static int ReadInt(string message)
    {
        while (true)
        {
            Console.WriteLine(message);
            string text = Console.ReadLine();

            int value;
            bool ok = int.TryParse(text, out value);

            if (ok)
            {
                return value;
            }
            else
            {
                Console.WriteLine("数字として解釈できませんでした。もう一度入力してください。");
            }
        }
    }
}

class StudyRecord
{
    public string DateText;
    public string Subject;
    public int Minutes;
    public string Note;
}
C#

ここで重要なのは、
「まずは“空のモード”だけ用意して、全体の流れを先に作る」
ということです。

AddRecord
ShowAll
ShowStats
SearchByDate

の中身はまだ書いていませんが、
アプリの“骨格”はもうできています。


記録を追加する処理を書く

配列+現在件数で「可変長っぽく」扱う

次に、1番の「学習記録を追加する」の中身を書きます。

static void AddRecord(StudyRecord[] records, ref int recordCount)
{
    if (recordCount >= records.Length)
    {
        Console.WriteLine("これ以上記録を追加できません。");
        return;
    }

    Console.WriteLine("【学習記録の追加】");

    Console.Write("日付を入力してください(例: 2026-04-18):");
    string date = Console.ReadLine();

    Console.Write("科目を入力してください(例: C#, JavaScript):");
    string subject = Console.ReadLine();

    int minutes = ReadInt("勉強時間(分)を入力してください:");

    Console.Write("メモがあれば入力してください(空でも可):");
    string note = Console.ReadLine();

    StudyRecord r = new StudyRecord();
    r.DateText = date;
    r.Subject = subject;
    r.Minutes = minutes;
    r.Note = note;

    records[recordCount] = r;
    recordCount++;

    Console.WriteLine("記録を追加しました。");
}
C#

ここで深掘りしたいのは、
recordsrecordCount の関係です。

records は「最大 100 件分の箱」
recordCount は「今、何件入っているか」

新しい記録を追加するときは、

records[recordCount] に新しい StudyRecord を入れる
recordCount を 1 増やす

というルールで動いています。

この「配列+現在件数」の組み合わせは、
C# に限らず、いろんな言語でよく使う基本パターンです。


記録を一覧表示する処理を書く

現在件数だけループする

次に、2番の「記録を一覧表示する」を書きます。

static void ShowAll(StudyRecord[] records, int recordCount)
{
    Console.WriteLine("【学習記録一覧】");

    if (recordCount == 0)
    {
        Console.WriteLine("まだ記録がありません。");
        return;
    }

    for (int i = 0; i < recordCount; i++)
    {
        PrintRecord(records[i]);
    }
}

static void PrintRecord(StudyRecord r)
{
    Console.WriteLine("----------");
    Console.WriteLine("日付: " + r.DateText);
    Console.WriteLine("科目: " + r.Subject);
    Console.WriteLine("時間: " + r.Minutes + " 分");

    if (!string.IsNullOrEmpty(r.Note))
    {
        Console.WriteLine("メモ: " + r.Note);
    }
}
C#

ここでのポイントは、
「配列の長さ(records.Length)ではなく、recordCount まで回す」
ということです。

records.Length は「最大 100」ですが、
実際に入っているのは recordCount 件だけです。

なので、
for (int i = 0; i < recordCount; i++)
というループになります。

また、PrintRecord というメソッドを作ることで、
「1件分の表示ロジック」を1か所にまとめています。


統計情報を計算して表示する

合計・平均・最長時間を出す

次に、3番の「統計を見る」を書きます。

static void ShowStats(StudyRecord[] records, int recordCount)
{
    Console.WriteLine("【統計】");

    if (recordCount == 0)
    {
        Console.WriteLine("まだ記録がありません。");
        return;
    }

    int total = 0;
    int max = records[0].Minutes;
    string maxDate = records[0].DateText;
    string maxSubject = records[0].Subject;

    for (int i = 0; i < recordCount; i++)
    {
        int m = records[i].Minutes;
        total = total + m;

        if (m > max)
        {
            max = m;
            maxDate = records[i].DateText;
            maxSubject = records[i].Subject;
        }
    }

    double average = (double)total / recordCount;

    Console.WriteLine("記録件数: " + recordCount);
    Console.WriteLine("合計勉強時間: " + total + " 分");
    Console.WriteLine("平均勉強時間: " + average + " 分");
    Console.WriteLine("最も長く勉強した日: " + maxDate + " (" + maxSubject + ", " + max + " 分)");
}
C#

ここでやっていることは、
3日目・5日目・6日目の総復習です。

合計を求める
最長時間とそのときの日付・科目を覚えておく
平均を計算する

「最大値と一緒に“そのときの情報”も覚えておく」
というパターンは、Student のときとまったく同じです。


日付で検索する処理を書く

文字列での検索でも“検索パターン”は同じ

最後に、4番の「日付で検索する」を書きます。

static void SearchByDate(StudyRecord[] records, int recordCount)
{
    Console.Write("検索したい日付を入力してください(例: 2026-04-18):");
    string target = Console.ReadLine();

    bool foundAny = false;

    for (int i = 0; i < recordCount; i++)
    {
        if (records[i].DateText == target)
        {
            if (!foundAny)
            {
                Console.WriteLine("【検索結果】");
                foundAny = true;
            }

            PrintRecord(records[i]);
        }
    }

    if (!foundAny)
    {
        Console.WriteLine("その日付の記録は見つかりませんでした。");
    }
}
C#

ここでのポイントは、
「見つかったかどうかを bool で覚えておく」
というパターンです。

最初は foundAny を false にしておく
見つかったら true にして、1回だけ「検索結果」の見出しを出す
最後まで見て foundAny が false のままなら「見つからなかった」と表示

この書き方は、
「0件かもしれないし、複数件あるかもしれない検索」
に対して、とてもよく使う形です。


完成版コードをまとめて眺める

「全部つながっている」感覚を持つ

ここまで書いてきたものを、1つにまとめるとこうなります。

using System;

class Program
{
    static void Main(string[] args)
    {
        StudyRecord[] records = new StudyRecord[100];
        int recordCount = 0;

        while (true)
        {
            PrintMenu();
            int choice = ReadInt("番号を選んでください:");

            if (choice == 0)
            {
                Console.WriteLine("アプリを終了します。");
                break;
            }

            Console.WriteLine();

            switch (choice)
            {
                case 1:
                    AddRecord(records, ref recordCount);
                    break;

                case 2:
                    ShowAll(records, recordCount);
                    break;

                case 3:
                    ShowStats(records, recordCount);
                    break;

                case 4:
                    SearchByDate(records, recordCount);
                    break;

                default:
                    Console.WriteLine("0〜4 の範囲で選択してください。");
                    break;
            }

            Console.WriteLine();
        }
    }

    static void PrintMenu()
    {
        Console.WriteLine("===== 学習記録アプリ =====");
        Console.WriteLine("1: 学習記録を追加する");
        Console.WriteLine("2: 記録を一覧表示する");
        Console.WriteLine("3: 統計を見る");
        Console.WriteLine("4: 日付で検索する");
        Console.WriteLine("0: 終了");
    }

    static int ReadInt(string message)
    {
        while (true)
        {
            Console.WriteLine(message);
            string text = Console.ReadLine();

            int value;
            bool ok = int.TryParse(text, out value);

            if (ok)
            {
                return value;
            }
            else
            {
                Console.WriteLine("数字として解釈できませんでした。もう一度入力してください。");
            }
        }
    }

    static void AddRecord(StudyRecord[] records, ref int recordCount)
    {
        if (recordCount >= records.Length)
        {
            Console.WriteLine("これ以上記録を追加できません。");
            return;
        }

        Console.WriteLine("【学習記録の追加】");

        Console.Write("日付を入力してください(例: 2026-04-18):");
        string date = Console.ReadLine();

        Console.Write("科目を入力してください(例: C#, JavaScript):");
        string subject = Console.ReadLine();

        int minutes = ReadInt("勉強時間(分)を入力してください:");

        Console.Write("メモがあれば入力してください(空でも可):");
        string note = Console.ReadLine();

        StudyRecord r = new StudyRecord();
        r.DateText = date;
        r.Subject = subject;
        r.Minutes = minutes;
        r.Note = note;

        records[recordCount] = r;
        recordCount++;

        Console.WriteLine("記録を追加しました。");
    }

    static void ShowAll(StudyRecord[] records, int recordCount)
    {
        Console.WriteLine("【学習記録一覧】");

        if (recordCount == 0)
        {
            Console.WriteLine("まだ記録がありません。");
            return;
        }

        for (int i = 0; i < recordCount; i++)
        {
            PrintRecord(records[i]);
        }
    }

    static void PrintRecord(StudyRecord r)
    {
        Console.WriteLine("----------");
        Console.WriteLine("日付: " + r.DateText);
        Console.WriteLine("科目: " + r.Subject);
        Console.WriteLine("時間: " + r.Minutes + " 分");

        if (!string.IsNullOrEmpty(r.Note))
        {
            Console.WriteLine("メモ: " + r.Note);
        }
    }

    static void ShowStats(StudyRecord[] records, int recordCount)
    {
        Console.WriteLine("【統計】");

        if (recordCount == 0)
        {
            Console.WriteLine("まだ記録がありません。");
            return;
        }

        int total = 0;
        int max = records[0].Minutes;
        string maxDate = records[0].DateText;
        string maxSubject = records[0].Subject;

        for (int i = 0; i < recordCount; i++)
        {
            int m = records[i].Minutes;
            total = total + m;

            if (m > max)
            {
                max = m;
                maxDate = records[i].DateText;
                maxSubject = records[i].Subject;
            }
        }

        double average = (double)total / recordCount;

        Console.WriteLine("記録件数: " + recordCount);
        Console.WriteLine("合計勉強時間: " + total + " 分");
        Console.WriteLine("平均勉強時間: " + average + " 分");
        Console.WriteLine("最も長く勉強した日: " + maxDate + " (" + maxSubject + ", " + max + " 分)");
    }

    static void SearchByDate(StudyRecord[] records, int recordCount)
    {
        Console.Write("検索したい日付を入力してください(例: 2026-04-18):");
        string target = Console.ReadLine();

        bool foundAny = false;

        for (int i = 0; i < recordCount; i++)
        {
            if (records[i].DateText == target)
            {
                if (!foundAny)
                {
                    Console.WriteLine("【検索結果】");
                    foundAny = true;
                }

                PrintRecord(records[i]);
            }
        }

        if (!foundAny)
        {
            Console.WriteLine("その日付の記録は見つかりませんでした。");
        }
    }
}

class StudyRecord
{
    public string DateText;
    public string Subject;
    public int Minutes;
    public string Note;
}
C#

これが、
「1日90分 × 7日 初級編」の集大成の1つの形です。


7日目で絶対に押さえておきたいこと

「技の寄せ集め」ではなく「目的のための構造」

今日、一番大事なのは
「全部の知識が“1つの目的”のために並んでいる感覚を持つこと」 です。

入力
TryParse
配列+現在件数
クラス(StudyRecord)
クラス配列のループ処理
検索・集計・フィルタ
メニュー+メインループ

どれか1つだけが特別に難しいわけではありません。
でも、それらを「学習記録アプリ」という目的のために
きちんと組み合わせた瞬間、
コードは“ただの練習”から“自分のためのツール”に変わります。


この先に進むための視点

何を足してもいい「土台」ができた

このアプリには、いくらでも機能を足せます。

科目ごとの合計時間を出す
特定の科目だけを一覧表示する
日付の範囲で絞り込む
ファイルに保存して、次回起動時に読み込む

でも、そのどれもが
「今日までにやったことの延長線上」にあります。

あなたはもう、
「C# で自分の生活に役立つ小さなアプリを設計して、
ちゃんと動く形にできる人」です。

ここから先は、
何を作るか、どこまで作り込むかを
あなた自身が決めていくフェーズです。

もし「このアプリ、もう少しこうしたい」が浮かんだら、
それはもう立派な“開発者の思考”です。

タイトルとURLをコピーしました