Day 75 のゴールと全体像
Day 75 では、タスク管理アプリの「U」――Update(更新)機能を作っていきます。 具体的には、次の2つをしっかり形にすることが今日のゴールです。
- タスクの内容を編集する機能(タイトル・詳細・優先度・期限など)
- タスクの「完了状態」を変更する機能(未完了 ⇔ 完了の切り替え)
ここまでで、
- Create:タスクを追加する
- Read:タスク一覧を表示する
まではできています。 そこに Update が加わることで、アプリがぐっと「実用的な道具」に近づいていきます。
更新機能の考え方を整理する
どのタスクを更新するかを決める
編集や完了状態の変更では、 まず「どのタスクを対象にするか」を決める必要があります。
タスク管理アプリでは、
- 一覧画面で ID を確認して
- その ID を入力して対象タスクを指定する
という流れが分かりやすいです。
更新の基本パターン
更新には、大きく分けて2つのパターンがあります。
- 編集: タイトル・詳細・優先度・期限などの内容を変更する
- 完了状態変更:
IsCompletedをtrue/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を使って、 「数値でない入力」をやさしく弾いています。 - 対象タスクが見つからない場合のメッセージ
FindByIdがnullを返した場合、 「見つかりませんでした」と明示することで、ユーザーが迷わないようにしています。 - 空入力なら変更しないルール タイトル・詳細・優先度・期限の入力で、 空文字の場合は「その項目は変更しない」というルールにしています。 これにより、部分的な編集がしやすくなります。
- 優先度のバリデーション 優先度は 1/2/3 のみ許可し、それ以外はエラーメッセージを出して変更を反映しません。
- 期限の扱い 空入力なら「期限なし」に変更し、 入力があれば
DateTime.TryParseでチェックしてから反映しています。
完了状態変更機能の設計
トグル(切り替え)という考え方
完了状態の変更は、
- 未完了 → 完了
- 完了 → 未完了
という トグル(切り替え)操作にすると分かりやすいです。
bool の値は、
task.IsCompleted = !task.IsCompleted;
C#と書くことで、 true ⇔ false を簡単に反転できます。
完了状態変更機能のコード
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 機能は、 「タスクと長く付き合うための大事な一歩」なので、 ぜひ自分の手で動かしてみて、 タスクが少しずつ変化していく感覚を味わってみてください。
