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を返しています。 これにより、削除機能を呼び出す側で、 「削除できたかどうか」をメッセージとして表示できます。
削除機能の流れを設計する
削除機能の流れを、言葉で整理してみます。
- 削除したいタスクの ID を入力してもらう
- ID が数値かどうかチェックする
- 該当するタスクが存在するか確認する
- タスクの内容を表示する
- 「本当に削除しますか?」と確認する
- ユーザーが「y」と答えたら削除する
- 結果をメッセージとして表示する
この流れを、そのままコードに落とし込んでいきます。
削除機能のコードを組み立てる
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の戻り値をチェックRemoveがtrueを返した場合だけ「削除しました」と表示し、 万が一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(削除)機能を丁寧に作り上げました。
TaskRepositoryにRemoveメソッドを追加し- IDで削除対象を指定する流れを作り
- 削除前にタスクの内容を表示して確認し
y/nで最終確認を行うことで、安全性を高め- メニューに「タスク削除」を組み込んで、CRUD を完成させました。
これで、タスク管理アプリは、
追加して 一覧で見て 編集して 完了にして いらなくなったら削除する
という、一連のライフサイクルをひと通り扱えるようになりました。
削除機能は少しドキドキする操作ですが、 きちんと確認のステップを挟むことで、 安心して使える機能に育てることができます。
ここまで来たみなさんのコードは、 もう「練習用のサンプル」を超えて、 立派な小さなプロダクトになりつつあります。 次のステップでは、このアプリをさらに磨いていく方向も、 新しいアプリに応用していく方向も、どちらも楽しくなっていきます。
