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

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

Day 76 のゴールと全体像

Day 76 では、タスク管理アプリの最後の柱、 「D」――Delete(削除)機能を作っていきます。

ここまでで、

  • Create:タスクを追加する
  • Read:タスク一覧を表示する
  • Update:タスクを編集したり、完了状態を変更する

までが揃いました。 今日の Delete が完成すると、いよいよ CRUD が一通りそろった「ちゃんとしたアプリ」になります。

削除機能は、便利である一方で、 「間違って消してしまう」という怖さもある機能です。 ですので、今日は 安全性と分かりやすさを意識しながら、 ステップバイステップで削除機能を育てていきます。

削除機能の役割を整理する

何を削除するのか

削除機能の役割は、とてもシンプルです。

  • 指定されたタスクを
  • タスク一覧から取り除く

ただし、その前に必ず必要になるのが、

「どのタスクを削除するのか」

を決めることです。

タスク管理アプリでは、 ID を使って削除対象を指定するのが分かりやすいです。

安全性のために考えたいこと

削除は「元に戻せない操作」になりがちです。

コンソールアプリでは、 簡易的に次のような工夫を入れておくと安心感が増します。

  • 削除前に対象タスクの内容を表示する
  • 「本当に削除しますか?」と確認する
  • キャンセルできるようにする

今日は、このあたりも含めて削除機能を設計していきます。

TaskRepository に削除メソッドを追加する

まずは、タスク一覧を管理している 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;
    }

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

    // 削除メソッド
    public bool Remove(int id)
    {
        // 指定されたIDのタスクを探す
        var task = FindById(id);
        if (task == null)
        {
            // 見つからなければ削除失敗
            return false;
        }

        // 見つかったタスクをリストから取り除く
        _tasks.Remove(task);
        return true;
    }
}
C#

重要ポイントの解説

  • FindById を再利用していること 削除対象を探すときに、 すでにある FindById を使うことで、 コードの重複を避けています。
  • 戻り値を bool にしていること 削除が成功したかどうかを呼び出し側に伝えるために、 true / false を返しています。 これにより、削除機能を呼び出す側で、 「削除できたかどうか」をメッセージとして表示できます。

削除機能の流れを設計する

削除機能の流れを、言葉で整理してみます。

  1. 削除したいタスクの ID を入力してもらう
  2. ID が数値かどうかチェックする
  3. 該当するタスクが存在するか確認する
  4. タスクの内容を表示する
  5. 「本当に削除しますか?」と確認する
  6. ユーザーが「y」と答えたら削除する
  7. 結果をメッセージとして表示する

この流れを、そのままコードに落とし込んでいきます。

削除機能のコードを組み立てる

Delete メソッドの実装

void DeleteTask()
{
    Console.WriteLine("=== タスク削除 ===");

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

    // 2. IDが数値かどうかチェック
    if (!int.TryParse(idText, out int id))
    {
        Console.WriteLine("IDは数値で入力してください。");
        return;
    }

    // 3. 該当するタスクが存在するか確認
    TaskItem task = repository.FindById(id);
    if (task == null)
    {
        Console.WriteLine("指定されたIDのタスクが見つかりませんでした。");
        return;
    }

    // 4. タスクの内容を表示
    Console.WriteLine("削除対象のタスク:");
    string status = task.IsCompleted ? "[完了]" : "[未完了]";
    string priorityText = task.Priority switch
    {
        1 => "高",
        2 => "中",
        3 => "低",
        _ => "不明"
    };
    string dueText = task.DueDate.HasValue
        ? task.DueDate.Value.ToString("yyyy-MM-dd")
        : "期限なし";

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

    // 5. 本当に削除するか確認
    Console.Write("本当に削除しますか? (y/n): ");
    string answer = Console.ReadLine();

    if (answer?.ToLower() != "y")
    {
        Console.WriteLine("削除をキャンセルしました。");
        return;
    }

    // 6. 削除処理を実行
    bool removed = repository.Remove(id);

    // 7. 結果を表示
    if (removed)
    {
        Console.WriteLine("タスクを削除しました。");
    }
    else
    {
        // 理論上ここに来ることはほぼありませんが、安全のために用意しておく
        Console.WriteLine("削除に失敗しました。");
    }
}
C#

コードのポイントを深掘り

  • ID入力のチェック int.TryParse を使うことで、 「数字ではない入力」をやさしく弾いています。 これにより、削除対象の指定ミスを減らせます。
  • 削除対象の内容表示 削除前にタスクの内容を表示することで、 「本当にこのタスクで合っているか」を確認できます。 これは、削除機能における大事な安全装置です。
  • y/n の確認 ユーザーが y と入力したときだけ削除を実行し、 それ以外はキャンセルするようにしています。 うっかり Enter を押してしまったときにも、 いきなり削除されないようにするための配慮です。
  • Remove の戻り値をチェック Removetrue を返した場合だけ「削除しました」と表示し、 万が一 false の場合は「削除に失敗しました」と表示しています。

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

コンソールメニューの完成形

Day 73〜75 で作ってきたメニューに、 削除機能を追加していきます。

void Run()
{
    while (true)
    {
        Console.WriteLine("=== タスク管理アプリ ===");
        Console.WriteLine("1: タスク追加");
        Console.WriteLine("2: タスク一覧");
        Console.WriteLine("3: タスク編集");
        Console.WriteLine("4: 完了状態の変更");
        Console.WriteLine("5: タスク削除");
        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 "5":
                DeleteTask();
                break;
            case "0":
                Console.WriteLine("終了します。");
                return;
            default:
                Console.WriteLine("不正な入力です。");
                break;
        }

        Console.WriteLine();
    }
}
C#

これで、

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

という、CRUD のすべてがメニューから操作できるようになります。

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

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);
    }

    public bool Remove(int id)
    {
        var task = FindById(id);
        if (task == null)
        {
            return false;
        }

        _tasks.Remove(task);
        return true;
    }
}

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("5: タスク削除");
            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 "5":
                    DeleteTask();
                    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($"  詳細   : {t.Description}");
            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 ? "完了" : "未完了")}");
    }

    static void DeleteTask()
    {
        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("削除対象のタスク:");
        string status = task.IsCompleted ? "[完了]" : "[未完了]";
        string priorityText = task.Priority switch
        {
            1 => "高",
            2 => "中",
            3 => "低",
            _ => "不明"
        };
        string dueText = task.DueDate.HasValue
            ? task.DueDate.Value.ToString("yyyy-MM-dd")
            : "期限なし";

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

        Console.Write("本当に削除しますか? (y/n): ");
        string answer = Console.ReadLine();

        if (answer?.ToLower() != "y")
        {
            Console.WriteLine("削除をキャンセルしました。");
            return;
        }

        bool removed = repository.Remove(id);

        if (removed)
        {
            Console.WriteLine("タスクを削除しました。");
        }
        else
        {
            Console.WriteLine("削除に失敗しました。");
        }
    }
}
C#

Day 76 のまとめ

Day 76 では、タスク管理アプリの Delete(削除)機能を丁寧に作り上げました。

  • TaskRepositoryRemove メソッドを追加し
  • IDで削除対象を指定する流れを作り
  • 削除前にタスクの内容を表示して確認し
  • y/n で最終確認を行うことで、安全性を高め
  • メニューに「タスク削除」を組み込んで、CRUD を完成させました。

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

追加して 一覧で見て 編集して 完了にして いらなくなったら削除する

という、一連のライフサイクルをひと通り扱えるようになりました。

削除機能は少しドキドキする操作ですが、 きちんと確認のステップを挟むことで、 安心して使える機能に育てることができます。

ここまで来たみなさんのコードは、 もう「練習用のサンプル」を超えて、 立派な小さなプロダクトになりつつあります。 次のステップでは、このアプリをさらに磨いていく方向も、 新しいアプリに応用していく方向も、どちらも楽しくなっていきます。

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