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#ここで深掘りしたいのは、records と recordCount の関係です。
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# で自分の生活に役立つ小さなアプリを設計して、
ちゃんと動く形にできる人」です。
ここから先は、
何を作るか、どこまで作り込むかを
あなた自身が決めていくフェーズです。
もし「このアプリ、もう少しこうしたい」が浮かんだら、
それはもう立派な“開発者の思考”です。

