基礎から学ぶC#入門 90日コース | 実践的なC#プログラミング - Day 60:中間総合アプリ

C# 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 60 のゴールと全体像

Day 60 は、第6章の「中間総合アプリ」として、 これまで学んできた C# の基礎・LINQ・ファイル操作・JSON をぎゅっと詰め込んだ 家計簿アプリ を題材にします。

扱う機能は次のとおりです。

  • 収入登録
  • 支出登録
  • カテゴリ
  • 合計
  • 月別集計
  • JSON保存
  • JSON読み込み

「ちょっとした実用アプリ」を自分の手で組み立てていくことで、 バラバラだった知識が一本の線でつながっていく感覚を味わっていただければと思います。

家計簿アプリの設計をざっくり決める

まずは、家計簿アプリの“心臓”になるデータ構造から考えていきます。

どんな情報を1件として扱うか

家計簿の1行には、だいたい次のような情報が入ります。

  • 日付
  • 金額
  • 種類(収入か支出か)
  • カテゴリ(食費・給料・光熱費など)
  • メモ(任意)

これを1つのクラスにまとめてみます。

using System;

class Transaction
{
    // 日付
    public DateTime Date { get; set; }

    // 金額(プラスでもマイナスでもなく、絶対値として扱う)
    public int Amount { get; set; }

    // 収入か支出か("Income" / "Expense" など)
    public string Type { get; set; }

    // カテゴリ(例:食費、給料、光熱費など)
    public string Category { get; set; }

    // メモ(任意)
    public string Note { get; set; }
}
C#

ここでは、

  • Type"Income"(収入)か "Expense"(支出)を入れる
  • Amount は「金額そのもの」で、収入か支出かは Type で区別する

という方針にしています。

家計簿データを管理する土台を作る

次に、家計簿の一覧を管理するためのリストを用意します。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static List<Transaction> transactions = new List<Transaction>();

    static void Main()
    {
        // ここにメニューや処理を追加していきます。
    }
}
C#

transactions が、家計簿の「全データ」を持つリストになります。 ここに「収入登録」「支出登録」でデータを追加し、 「合計」「月別集計」で LINQ を使って集計していく、という流れです。

収入登録・支出登録の処理を作る

まずは、家計簿の基本である「1件登録」を作っていきます。

入力用のヘルパーメソッド

コンソールから入力する形で、 シンプルな登録機能を作ってみます。

static void AddTransaction(string type)
{
    Console.WriteLine($"[{type} 登録]");

    // 日付の入力
    Console.Write("日付 (yyyy-MM-dd): ");
    var dateText = Console.ReadLine();
    DateTime date = DateTime.Parse(dateText); // 簡易的に Parse を使用

    // 金額の入力
    Console.Write("金額: ");
    var amountText = Console.ReadLine();
    int amount = int.Parse(amountText);

    // カテゴリの入力
    Console.Write("カテゴリ: ");
    string category = Console.ReadLine();

    // メモの入力
    Console.Write("メモ(任意): ");
    string note = Console.ReadLine();

    // Transaction オブジェクトを作成
    var t = new Transaction
    {
        Date = date,
        Amount = amount,
        Type = type,      // "Income" または "Expense"
        Category = category,
        Note = note
    };

    // リストに追加
    transactions.Add(t);

    Console.WriteLine("登録しました。");
    Console.WriteLine();
}
C#

収入登録・支出登録をメニューから呼び出す

static void Main()
{
    while (true)
    {
        Console.WriteLine("=== 家計簿アプリ ===");
        Console.WriteLine("1: 収入登録");
        Console.WriteLine("2: 支出登録");
        Console.WriteLine("3: 合計表示");
        Console.WriteLine("4: 月別集計表示");
        Console.WriteLine("5: JSON保存");
        Console.WriteLine("6: JSON読み込み");
        Console.WriteLine("0: 終了");
        Console.Write("番号を選んでください: ");

        var input = Console.ReadLine();
        Console.WriteLine();

        switch (input)
        {
            case "1":
                AddTransaction("Income");
                break;
            case "2":
                AddTransaction("Expense");
                break;
            case "3":
                ShowTotal();
                break;
            case "4":
                ShowMonthlySummary();
                break;
            case "5":
                SaveToJson();
                break;
            case "6":
                LoadFromJson();
                break;
            case "0":
                return;
            default:
                Console.WriteLine("不正な入力です。");
                break;
        }
    }
}
C#

これで、 「収入登録」「支出登録」の入口ができました。

合計を計算する ― 収入合計・支出合計・差額

次に、登録されたデータから「合計」を計算してみます。

LINQ を使った合計計算

static void ShowTotal()
{
    // 収入の合計
    int incomeTotal = transactions
        .Where(t => t.Type == "Income")
        .Sum(t => t.Amount);

    // 支出の合計
    int expenseTotal = transactions
        .Where(t => t.Type == "Expense")
        .Sum(t => t.Amount);

    // 差額(収支)
    int balance = incomeTotal - expenseTotal;

    Console.WriteLine("=== 合計 ===");
    Console.WriteLine($"収入合計: {incomeTotal} 円");
    Console.WriteLine($"支出合計: {expenseTotal} 円");
    Console.WriteLine($"収支: {balance} 円");
    Console.WriteLine();
}
C#

ここで押さえておきたいポイント

  • Where(t => t.Type == "Income") で「収入だけ」を絞り込み、Sum で合計を求めています。
  • 同様に「支出だけ」を絞り込んで合計を求め、
  • 最後に 収入合計 - 支出合計 で「収支」を計算しています。

LINQ の Where + Sum の組み合わせは、 家計簿のような「条件付き集計」でとてもよく使われるパターンです。

月別集計を作る ― 年月ごとにまとめる

次は、家計簿アプリらしい機能のひとつ「月別集計」を作っていきます。

年月ごとにグループ分けする

DateTime から「年」と「月」を取り出し、 それをキーに GroupBy でまとめます。

static void ShowMonthlySummary()
{
    // 年月ごとにグループ分け
    var groups = transactions
        .GroupBy(t => new { t.Date.Year, t.Date.Month });

    Console.WriteLine("=== 月別集計 ===");

    foreach (var group in groups)
    {
        int year = group.Key.Year;
        int month = group.Key.Month;

        // その月の収入合計
        int incomeTotal = group
            .Where(t => t.Type == "Income")
            .Sum(t => t.Amount);

        // その月の支出合計
        int expenseTotal = group
            .Where(t => t.Type == "Expense")
            .Sum(t => t.Amount);

        int balance = incomeTotal - expenseTotal;

        Console.WriteLine($"{year}年{month}月:");
        Console.WriteLine($"  収入合計: {incomeTotal} 円");
        Console.WriteLine($"  支出合計: {expenseTotal} 円");
        Console.WriteLine($"  収支: {balance} 円");
        Console.WriteLine();
    }
}
C#

月別集計のポイント

  • GroupBy(t => new { t.Date.Year, t.Date.Month }) → 「年と月の組み合わせ」をキーにグループ分けしています。
  • group の中には「その年月に属するすべての Transaction」が入っています。
  • その中でさらに Where + Sum を使うことで、 「月ごとの収入合計」「月ごとの支出合計」を求めています。

LINQ の GroupBy は、 こうした「月別」「カテゴリ別」「ユーザー別」などの集計で大活躍します。

JSON保存 ― 家計簿データをファイルに書き出す

次に、家計簿データを JSON 形式で保存する機能を作ります。

JSON シリアライズの準備

.NET では System.Text.Json を使うと、 オブジェクトを JSON に変換(シリアライズ)できます。

using System.Text.Json;
using System.IO;
C#

JSON保存メソッド

static void SaveToJson()
{
    Console.Write("保存するファイル名(例: kakeibo.json): ");
    string fileName = Console.ReadLine();

    // オブジェクト → JSON文字列 に変換
    var options = new JsonSerializerOptions
    {
        WriteIndented = true // 見やすい整形済みJSONにする
    };

    string json = JsonSerializer.Serialize(transactions, options);

    // ファイルに書き込み
    File.WriteAllText(fileName, json);

    Console.WriteLine("JSONファイルに保存しました。");
    Console.WriteLine();
}
C#

ポイント

  • JsonSerializer.Serialize(transactions, options) で、 List<Transaction> を JSON 文字列に変換しています。
  • WriteIndented = true を指定すると、 人間が読んでも見やすい形で整形された JSON になります。
  • File.WriteAllText(fileName, json) で、 その JSON をファイルに書き込んでいます。

JSON読み込み ― ファイルから家計簿データを復元する

次に、保存した JSON ファイルからデータを読み込む機能を作ります。

JSON読み込みメソッド

static void LoadFromJson()
{
    Console.Write("読み込むファイル名(例: kakeibo.json): ");
    string fileName = Console.ReadLine();

    if (!File.Exists(fileName))
    {
        Console.WriteLine("ファイルが見つかりません。");
        Console.WriteLine();
        return;
    }

    // ファイルからJSON文字列を読み込む
    string json = File.ReadAllText(fileName);

    // JSON文字列 → オブジェクト に変換
    var list = JsonSerializer.Deserialize<List<Transaction>>(json);

    if (list == null)
    {
        Console.WriteLine("読み込みに失敗しました。");
    }
    else
    {
        transactions = list;
        Console.WriteLine("JSONファイルから読み込みました。");
    }

    Console.WriteLine();
}
C#

ポイント

  • File.Exists(fileName) で、ファイルの存在チェックをしています。
  • JsonSerializer.Deserialize<List<Transaction>>(json) で、 JSON 文字列から List<Transaction> に復元しています。
  • 読み込んだリストを transactions に代入することで、 家計簿アプリの「現在のデータ」として扱えるようになります。

家計簿アプリの完成版テンプレート

ここまでのコードをひとつにまとめた、 「Day 60 家計簿アプリ」のテンプレートを紹介します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.Json;

class Transaction
{
    public DateTime Date { get; set; }   // 日付
    public int Amount { get; set; }      // 金額
    public string Type { get; set; }     // "Income" or "Expense"
    public string Category { get; set; } // カテゴリ
    public string Note { get; set; }     // メモ
}

class Program
{
    static List<Transaction> transactions = new List<Transaction>();

    static void Main()
    {
        while (true)
        {
            Console.WriteLine("=== 家計簿アプリ ===");
            Console.WriteLine("1: 収入登録");
            Console.WriteLine("2: 支出登録");
            Console.WriteLine("3: 合計表示");
            Console.WriteLine("4: 月別集計表示");
            Console.WriteLine("5: JSON保存");
            Console.WriteLine("6: JSON読み込み");
            Console.WriteLine("0: 終了");
            Console.Write("番号を選んでください: ");

            var input = Console.ReadLine();
            Console.WriteLine();

            switch (input)
            {
                case "1":
                    AddTransaction("Income");
                    break;
                case "2":
                    AddTransaction("Expense");
                    break;
                case "3":
                    ShowTotal();
                    break;
                case "4":
                    ShowMonthlySummary();
                    break;
                case "5":
                    SaveToJson();
                    break;
                case "6":
                    LoadFromJson();
                    break;
                case "0":
                    return;
                default:
                    Console.WriteLine("不正な入力です。");
                    Console.WriteLine();
                    break;
            }
        }
    }

    static void AddTransaction(string type)
    {
        Console.WriteLine($"[{type} 登録]");

        Console.Write("日付 (yyyy-MM-dd): ");
        var dateText = Console.ReadLine();
        DateTime date = DateTime.Parse(dateText); // 簡易的な変換

        Console.Write("金額: ");
        var amountText = Console.ReadLine();
        int amount = int.Parse(amountText);

        Console.Write("カテゴリ: ");
        string category = Console.ReadLine();

        Console.Write("メモ(任意): ");
        string note = Console.ReadLine();

        var t = new Transaction
        {
            Date = date,
            Amount = amount,
            Type = type,
            Category = category,
            Note = note
        };

        transactions.Add(t);

        Console.WriteLine("登録しました。");
        Console.WriteLine();
    }

    static void ShowTotal()
    {
        int incomeTotal = transactions
            .Where(t => t.Type == "Income")
            .Sum(t => t.Amount);

        int expenseTotal = transactions
            .Where(t => t.Type == "Expense")
            .Sum(t => t.Amount);

        int balance = incomeTotal - expenseTotal;

        Console.WriteLine("=== 合計 ===");
        Console.WriteLine($"収入合計: {incomeTotal} 円");
        Console.WriteLine($"支出合計: {expenseTotal} 円");
        Console.WriteLine($"収支: {balance} 円");
        Console.WriteLine();
    }

    static void ShowMonthlySummary()
    {
        var groups = transactions
            .GroupBy(t => new { t.Date.Year, t.Date.Month });

        Console.WriteLine("=== 月別集計 ===");

        foreach (var group in groups)
        {
            int year = group.Key.Year;
            int month = group.Key.Month;

            int incomeTotal = group
                .Where(t => t.Type == "Income")
                .Sum(t => t.Amount);

            int expenseTotal = group
                .Where(t => t.Type == "Expense")
                .Sum(t => t.Amount);

            int balance = incomeTotal - expenseTotal;

            Console.WriteLine($"{year}年{month}月:");
            Console.WriteLine($"  収入合計: {incomeTotal} 円");
            Console.WriteLine($"  支出合計: {expenseTotal} 円");
            Console.WriteLine($"  収支: {balance} 円");
            Console.WriteLine();
        }
    }

    static void SaveToJson()
    {
        Console.Write("保存するファイル名(例: kakeibo.json): ");
        string fileName = Console.ReadLine();

        var options = new JsonSerializerOptions
        {
            WriteIndented = true
        };

        string json = JsonSerializer.Serialize(transactions, options);

        File.WriteAllText(fileName, json);

        Console.WriteLine("JSONファイルに保存しました。");
        Console.WriteLine();
    }

    static void LoadFromJson()
    {
        Console.Write("読み込むファイル名(例: kakeibo.json): ");
        string fileName = Console.ReadLine();

        if (!File.Exists(fileName))
        {
            Console.WriteLine("ファイルが見つかりません。");
            Console.WriteLine();
            return;
        }

        string json = File.ReadAllText(fileName);

        var list = JsonSerializer.Deserialize<List<Transaction>>(json);

        if (list == null)
        {
            Console.WriteLine("読み込みに失敗しました。");
        }
        else
        {
            transactions = list;
            Console.WriteLine("JSONファイルから読み込みました。");
        }

        Console.WriteLine();
    }
}
C#

Day 60 のまとめ

Day 60 では、

  • 家計簿の1件を表す Transaction クラスを設計し
  • List<Transaction> で家計簿全体を管理し
  • 収入登録・支出登録でデータを追加し
  • LINQ を使って「合計」「月別集計」を計算し
  • System.Text.JsonFile クラスで「JSON保存」「JSON読み込み」を行う

という流れで、 小さな 家計簿アプリ を組み立てていきました。

ここまで来ると、

  • 「クラス設計」
  • 「LINQによる集計」
  • 「ファイル操作」
  • 「JSONシリアライズ/デシリアライズ」

といった要素が、ひとつのアプリの中で自然につながっているのが感じられると思います。

ぜひ、

  • カテゴリごとの集計を追加してみる
  • 月別集計を「特定の年だけ」に絞り込んでみる
  • 入力チェックや例外処理を加えて、少し堅牢なアプリに育ててみる

といった一歩を重ねながら、 「自分の手で作ったアプリ」が少しずつ育っていく感覚を楽しんでいただければうれしいです。

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