基礎から学ぶC#入門 90日コース | アプリを作りながら学ぶ - Day 75:Update

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

Day 75 のゴールと全体像

Day 75 では、タスク管理アプリの「U」――Update(更新)機能を作っていきます。 具体的には、次の2つをしっかり形にすることが今日のゴールです。

  • タスクの内容を編集する機能(タイトル・詳細・優先度・期限など)
  • タスクの「完了状態」を変更する機能(未完了 ⇔ 完了の切り替え)

ここまでで、

  • Create:タスクを追加する
  • Read:タスク一覧を表示する

まではできています。 そこに Update が加わることで、アプリがぐっと「実用的な道具」に近づいていきます。

更新機能の考え方を整理する

どのタスクを更新するかを決める

編集や完了状態の変更では、 まず「どのタスクを対象にするか」を決める必要があります。

タスク管理アプリでは、

  • 一覧画面で ID を確認して
  • その ID を入力して対象タスクを指定する

という流れが分かりやすいです。

更新の基本パターン

更新には、大きく分けて2つのパターンがあります。

  • 編集: タイトル・詳細・優先度・期限などの内容を変更する
  • 完了状態変更IsCompletedtrue / false の間で切り替える

今日は、この2つをそれぞれメソッドとして実装していきます。

TaskRepository に「検索」の入り口を用意する

まずは、更新対象のタスクを探すためのメソッドを用意しておきます。

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

// タスク一覧を管理するクラス
class TaskRepository
{
    private readonly List<TaskItem> _tasks = new List<TaskItem>();
    private int _nextId = 1;

    public TaskItem Add(string title, string description, int priority, DateTime? dueDate)
    {
        var task = new TaskItem
        {
            Id = _nextId++,
            Title = title,
            Description = description,
            Priority = priority,
            DueDate = dueDate,
            IsCompleted = false,
            CreatedAt = DateTime.Now
        };

        _tasks.Add(task);
        return task;
    }

    public IEnumerable<TaskItem> GetAll()
    {
        return _tasks;
    }

    // IDでタスクを検索するメソッド
    public TaskItem FindById(int id)
    {
        return _tasks.FirstOrDefault(t => t.Id == id);
    }
}
C#

FindById は、

  • 編集対象のタスクを探す
  • 完了状態を変更する対象を探す

といった場面で何度も登場する、重要な入り口になります。

編集機能の設計:何をどう変えるか

編集で変更したい項目

編集機能では、次の項目を変更できるようにしてみます。

  • タイトル(Title)
  • 詳細(Description)
  • 優先度(Priority)
  • 期限(DueDate)

すべて必ず変更しなければならないわけではなく、

「ここは変えたいけど、ここはそのままでいい」

ということも多いので、 「空入力なら変更しない」というルールにすると使いやすくなります。

編集機能のコードを組み立てる

編集メソッドの全体像

void EditTask()
{
    Console.WriteLine("=== タスク編集 ===");

    // 1. 編集したいタスクのIDを入力してもらう
    Console.Write("編集したいタスクのIDを入力してください: ");
    string idText = Console.ReadLine();

    if (!int.TryParse(idText, out int id))
    {
        Console.WriteLine("IDは数値で入力してください。");
        return;
    }

    // 2. リポジトリから該当タスクを探す
    TaskItem task = repository.FindById(id);
    if (task == null)
    {
        Console.WriteLine("指定されたIDのタスクが見つかりませんでした。");
        return;
    }

    // 3. 現在の内容を表示して、変更するかどうかを聞く
    Console.WriteLine($"現在のタイトル: {task.Title}");
    Console.Write("新しいタイトル(変更しない場合は空のまま): ");
    string newTitle = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(newTitle))
    {
        task.Title = newTitle;
    }

    Console.WriteLine($"現在の詳細: {task.Description}");
    Console.Write("新しい詳細(変更しない場合は空のまま): ");
    string newDescription = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(newDescription))
    {
        task.Description = newDescription;
    }

    Console.WriteLine($"現在の優先度: {task.Priority} (1: 高, 2: 中, 3: 低)");
    Console.Write("新しい優先度 (1/2/3、変更しない場合は空のまま): ");
    string newPriorityText = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(newPriorityText))
    {
        if (int.TryParse(newPriorityText, out int newPriority))
        {
            if (newPriority == 1 || newPriority == 2 || newPriority == 3)
            {
                task.Priority = newPriority;
            }
            else
            {
                Console.WriteLine("優先度は 1, 2, 3 のいずれかである必要があります。変更は反映されませんでした。");
            }
        }
        else
        {
            Console.WriteLine("優先度は数値で入力してください。変更は反映されませんでした。");
        }
    }

    Console.WriteLine($"現在の期限: {(task.DueDate.HasValue ? task.DueDate.Value.ToString("yyyy-MM-dd") : "期限なし")}");
    Console.Write("新しい期限 (yyyy-MM-dd、空なら期限なしに変更): ");
    string newDueText = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(newDueText))
    {
        // 空入力なら「期限なし」にする
        task.DueDate = null;
    }
    else
    {
        if (DateTime.TryParse(newDueText, out DateTime newDue))
        {
            task.DueDate = newDue;
        }
        else
        {
            Console.WriteLine("日付の形式が正しくありません。期限は変更されませんでした。");
        }
    }

    Console.WriteLine("タスクを更新しました。");
}
C#

重要ポイントの解説

  • ID入力のチェック int.TryParse を使って、 「数値でない入力」をやさしく弾いています。
  • 対象タスクが見つからない場合のメッセージ FindByIdnull を返した場合、 「見つかりませんでした」と明示することで、ユーザーが迷わないようにしています。
  • 空入力なら変更しないルール タイトル・詳細・優先度・期限の入力で、 空文字の場合は「その項目は変更しない」というルールにしています。 これにより、部分的な編集がしやすくなります。
  • 優先度のバリデーション 優先度は 1/2/3 のみ許可し、それ以外はエラーメッセージを出して変更を反映しません。
  • 期限の扱い 空入力なら「期限なし」に変更し、 入力があれば DateTime.TryParse でチェックしてから反映しています。

完了状態変更機能の設計

トグル(切り替え)という考え方

完了状態の変更は、

  • 未完了 → 完了
  • 完了 → 未完了

という トグル(切り替え)操作にすると分かりやすいです。

bool の値は、

task.IsCompleted = !task.IsCompleted;
C#

と書くことで、 truefalse を簡単に反転できます。

完了状態変更機能のコード

void ToggleComplete()
{
    Console.WriteLine("=== 完了状態の変更 ===");

    // 1. 対象タスクのIDを入力してもらう
    Console.Write("完了状態を切り替えたいタスクのIDを入力してください: ");
    string idText = Console.ReadLine();

    if (!int.TryParse(idText, out int id))
    {
        Console.WriteLine("IDは数値で入力してください。");
        return;
    }

    // 2. リポジトリから該当タスクを探す
    TaskItem task = repository.FindById(id);
    if (task == null)
    {
        Console.WriteLine("指定されたIDのタスクが見つかりませんでした。");
        return;
    }

    // 3. 現在の状態を表示
    Console.WriteLine($"現在の状態: {(task.IsCompleted ? "完了" : "未完了")}");

    // 4. 切り替えるかどうか確認(任意で入れてみる)
    Console.Write("状態を切り替えますか? (y/n): ");
    string answer = Console.ReadLine();

    if (answer?.ToLower() != "y")
    {
        Console.WriteLine("完了状態の変更をキャンセルしました。");
        return;
    }

    // 5. 完了状態を反転
    task.IsCompleted = !task.IsCompleted;

    // 6. 結果を表示
    Console.WriteLine($"完了状態を変更しました。現在の状態: {(task.IsCompleted ? "完了" : "未完了")}");
}
C#

ここで意識していること

  • ID指定の流れは編集と同じ 編集機能と同じく、IDで対象タスクを指定しています。 この「IDで指定する」というパターンは、アプリ全体で統一しておくと分かりやすいです。
  • 確認のひと手間 y/n で確認を入れることで、 「うっかり操作」を防ぐことができます。
  • トグル操作のシンプルさ task.IsCompleted = !task.IsCompleted; という一行で、完了状態を反転できるのは、 bool 型の気持ちよさのひとつです。

メニューに Update 機能を組み込む

コンソールメニューの拡張

Day 73・74 のメニューに、編集と完了状態変更を追加してみます。

void Run()
{
    while (true)
    {
        Console.WriteLine("=== タスク管理アプリ ===");
        Console.WriteLine("1: タスク追加");
        Console.WriteLine("2: タスク一覧");
        Console.WriteLine("3: タスク編集");
        Console.WriteLine("4: 完了状態の変更");
        Console.WriteLine("0: 終了");
        Console.Write("選択してください: ");

        string input = Console.ReadLine();

        switch (input)
        {
            case "1":
                CreateTask();
                break;
            case "2":
                ShowTasks();
                break;
            case "3":
                EditTask();
                break;
            case "4":
                ToggleComplete();
                break;
            case "0":
                Console.WriteLine("終了します。");
                return;
            default:
                Console.WriteLine("不正な入力です。");
                break;
        }

        Console.WriteLine();
    }
}
C#

これで、

  • 1:追加(Create)
  • 2:一覧(Read)
  • 3:編集(Update)
  • 4:完了状態変更(Updateの一種)

という、CRUD のうち CRU までが揃ったことになります。

Day 75 用ミニテンプレート(まとめて貼れるサンプル)

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

// タスクを表すクラス
class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int Priority { get; set; }
    public DateTime? DueDate { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }
}

// タスク一覧を管理するクラス
class TaskRepository
{
    private readonly List<TaskItem> _tasks = new List<TaskItem>();
    private int _nextId = 1;

    public TaskItem Add(string title, string description, int priority, DateTime? dueDate)
    {
        var task = new TaskItem
        {
            Id = _nextId++,
            Title = title,
            Description = description,
            Priority = priority,
            DueDate = dueDate,
            IsCompleted = false,
            CreatedAt = DateTime.Now
        };

        _tasks.Add(task);
        return task;
    }

    public IEnumerable<TaskItem> GetAll()
    {
        return _tasks;
    }

    public TaskItem FindById(int id)
    {
        return _tasks.FirstOrDefault(t => t.Id == id);
    }
}

class Program
{
    static TaskRepository repository = new TaskRepository();

    static void Main()
    {
        Run();
    }

    static void Run()
    {
        while (true)
        {
            Console.WriteLine("=== タスク管理アプリ ===");
            Console.WriteLine("1: タスク追加");
            Console.WriteLine("2: タスク一覧");
            Console.WriteLine("3: タスク編集");
            Console.WriteLine("4: 完了状態の変更");
            Console.WriteLine("0: 終了");
            Console.Write("選択してください: ");

            string input = Console.ReadLine();

            switch (input)
            {
                case "1":
                    CreateTask();
                    break;
                case "2":
                    ShowTasks();
                    break;
                case "3":
                    EditTask();
                    break;
                case "4":
                    ToggleComplete();
                    break;
                case "0":
                    Console.WriteLine("終了します。");
                    return;
                default:
                    Console.WriteLine("不正な入力です。");
                    break;
            }

            Console.WriteLine();
        }
    }

    static void CreateTask()
    {
        Console.WriteLine("=== タスク追加 ===");

        string title = ReadTitle();
        string description = ReadDescription();
        int priority = ReadPriority();
        DateTime? dueDate = ReadDueDate();

        TaskItem task = repository.Add(title, description, priority, dueDate);

        Console.WriteLine($"タスクを追加しました。ID: {task.Id}");
    }

    static string ReadTitle()
    {
        while (true)
        {
            Console.Write("タスク名を入力してください: ");
            string title = Console.ReadLine();

            if (!string.IsNullOrWhiteSpace(title))
            {
                return title;
            }

            Console.WriteLine("タスク名は必須です。空では登録できません。");
        }
    }

    static string ReadDescription()
    {
        Console.Write("詳細(メモ)を入力してください(空でも構いません): ");
        string description = Console.ReadLine();
        return description ?? string.Empty;
    }

    static int ReadPriority()
    {
        while (true)
        {
            Console.Write("優先度を入力してください (1: 高, 2: 中, 3: 低): ");
            string text = Console.ReadLine();

            if (int.TryParse(text, out int value))
            {
                if (value == 1 || value == 2 || value == 3)
                {
                    return value;
                }
            }

            Console.WriteLine("優先度は 1, 2, 3 のいずれかで入力してください。");
        }
    }

    static DateTime? ReadDueDate()
    {
        while (true)
        {
            Console.Write("期限を入力してください (yyyy-MM-dd、未入力なら期限なし): ");
            string text = Console.ReadLine();

            if (string.IsNullOrWhiteSpace(text))
            {
                return null;
            }

            if (DateTime.TryParse(text, out DateTime date))
            {
                return date;
            }

            Console.WriteLine("日付の形式が正しくありません。例: 2026-09-10 のように入力してください。");
        }
    }

    static void ShowTasks()
    {
        Console.WriteLine("=== タスク一覧 ===");

        var tasks = repository.GetAll();

        if (!tasks.Any())
        {
            Console.WriteLine("タスクが登録されていません。");
            return;
        }

        foreach (var t in tasks)
        {
            string status = t.IsCompleted ? "[完了]" : "[未完了]";
            string priorityText = t.Priority switch
            {
                1 => "高",
                2 => "中",
                3 => "低",
                _ => "不明"
            };
            string dueText = t.DueDate.HasValue
                ? t.DueDate.Value.ToString("yyyy-MM-dd")
                : "期限なし";

            Console.WriteLine($"ID: {t.Id}");
            Console.WriteLine($"  状態   : {status}");
            Console.WriteLine($"  優先度 : {priorityText}");
            Console.WriteLine($"  期限   : {dueText}");
            Console.WriteLine($"  タイトル: {t.Title}");
            Console.WriteLine();
        }
    }

    static void EditTask()
    {
        Console.WriteLine("=== タスク編集 ===");

        Console.Write("編集したいタスクのIDを入力してください: ");
        string idText = Console.ReadLine();

        if (!int.TryParse(idText, out int id))
        {
            Console.WriteLine("IDは数値で入力してください。");
            return;
        }

        TaskItem task = repository.FindById(id);
        if (task == null)
        {
            Console.WriteLine("指定されたIDのタスクが見つかりませんでした。");
            return;
        }

        Console.WriteLine($"現在のタイトル: {task.Title}");
        Console.Write("新しいタイトル(変更しない場合は空のまま): ");
        string newTitle = Console.ReadLine();
        if (!string.IsNullOrWhiteSpace(newTitle))
        {
            task.Title = newTitle;
        }

        Console.WriteLine($"現在の詳細: {task.Description}");
        Console.Write("新しい詳細(変更しない場合は空のまま): ");
        string newDescription = Console.ReadLine();
        if (!string.IsNullOrWhiteSpace(newDescription))
        {
            task.Description = newDescription;
        }

        Console.WriteLine($"現在の優先度: {task.Priority} (1: 高, 2: 中, 3: 低)");
        Console.Write("新しい優先度 (1/2/3、変更しない場合は空のまま): ");
        string newPriorityText = Console.ReadLine();
        if (!string.IsNullOrWhiteSpace(newPriorityText))
        {
            if (int.TryParse(newPriorityText, out int newPriority))
            {
                if (newPriority == 1 || newPriority == 2 || newPriority == 3)
                {
                    task.Priority = newPriority;
                }
                else
                {
                    Console.WriteLine("優先度は 1, 2, 3 のいずれかである必要があります。変更は反映されませんでした。");
                }
            }
            else
            {
                Console.WriteLine("優先度は数値で入力してください。変更は反映されませんでした。");
            }
        }

        Console.WriteLine($"現在の期限: {(task.DueDate.HasValue ? task.DueDate.Value.ToString("yyyy-MM-dd") : "期限なし")}");
        Console.Write("新しい期限 (yyyy-MM-dd、空なら期限なしに変更): ");
        string newDueText = Console.ReadLine();
        if (string.IsNullOrWhiteSpace(newDueText))
        {
            task.DueDate = null;
        }
        else
        {
            if (DateTime.TryParse(newDueText, out DateTime newDue))
            {
                task.DueDate = newDue;
            }
            else
            {
                Console.WriteLine("日付の形式が正しくありません。期限は変更されませんでした。");
            }
        }

        Console.WriteLine("タスクを更新しました。");
    }

    static void ToggleComplete()
    {
        Console.WriteLine("=== 完了状態の変更 ===");

        Console.Write("完了状態を切り替えたいタスクのIDを入力してください: ");
        string idText = Console.ReadLine();

        if (!int.TryParse(idText, out int id))
        {
            Console.WriteLine("IDは数値で入力してください。");
            return;
        }

        TaskItem task = repository.FindById(id);
        if (task == null)
        {
            Console.WriteLine("指定されたIDのタスクが見つかりませんでした。");
            return;
        }

        Console.WriteLine($"現在の状態: {(task.IsCompleted ? "完了" : "未完了")}");
        Console.Write("状態を切り替えますか? (y/n): ");
        string answer = Console.ReadLine();

        if (answer?.ToLower() != "y")
        {
            Console.WriteLine("完了状態の変更をキャンセルしました。");
            return;
        }

        task.IsCompleted = !task.IsCompleted;

        Console.WriteLine($"完了状態を変更しました。現在の状態: {(task.IsCompleted ? "完了" : "未完了")}");
    }
}
C#

Day 75 のまとめ

Day 75 では、タスク管理アプリの Update(更新)機能をしっかり形にしました。

  • IDで対象タスクを指定する流れを作り
  • タイトル・詳細・優先度・期限を編集できる機能を実装し
  • 完了状態をトグルで切り替える機能を追加し
  • メニューに「編集」「完了状態変更」を組み込んで、 アプリ全体の操作の流れを整えました。

これで、タスク管理アプリは、

追加して 一覧で見て 必要に応じて編集して 終わったら完了にする

という、現実のタスク管理にかなり近い動きをするようになりました。

次のステップでは、 残る「D」――削除(Delete)や、 検索・並び替えなどの機能を加えていくことで、 さらに使い心地の良いアプリへと育てていきます。

今日の Update 機能は、 「タスクと長く付き合うための大事な一歩」なので、 ぜひ自分の手で動かしてみて、 タスクが少しずつ変化していく感覚を味わってみてください。

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