C#: 総合プロジェクト:タスクマネージャー

1. 要件分析

以下の機能を備えたコマンドラインタスクマネージャーを構築します:

この機能は7つのステップに分けられ、段階的に実装されています。


2. クラスの設計:列挙型とデータモデル

まず、優先度の列挙型とタスクエンティティクラスを定義します。

▶ サンプル

CSHARP
enum Priority
{
    Low,
    Medium,
    High
}

class Task
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Priority Priority { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }

    public Task(int id, string title, string description, Priority priority)
    {
        Id = id;
        Title = title;
        Description = description;
        Priority = priority;
        IsCompleted = false;
        CreatedAt = DateTime.Now;
    }

    public override string ToString()
    {
        string status = IsCompleted ? "[x]" : "[ ]";
        return $"{Id,-3} {status} {Priority,-6} {CreatedAt:yyyy-MM-dd} {Title}";
    }
}
▶ 試してみよう

3. コレクションの保存:TaskManager と CRUD 操作

List<Task>を使用してタスクを保存し、基本的な作成、読み取り、更新、削除の操作を実行します。

▶ サンプル

CSHARP
class TaskManager
{
    private List<Task> tasks = new List<Task>();
    private int nextId = 1;

    public Task AddTask(string title, string description, Priority priority)
    {
        Task task = new Task(nextId++, title, description, priority);
        tasks.Add(task);
        return task;
    }

    public bool RemoveTask(int id)
    {
        Task task = tasks.Find(t => t.Id == id);
        if (task != null)
        {
            tasks.Remove(task);
            return true;
        }
        return false;
    }

    public bool CompleteTask(int id)
    {
        Task task = tasks.Find(t => t.Id == id);
        if (task != null)
        {
            task.IsCompleted = true;
            return true;
        }
        return false;
    }

    public List<Task> GetAllTasks()
    {
        return new List<Task>(tasks);
    }

    public void SortByPriority()
    {
        tasks.Sort((a, b) => b.Priority.CompareTo(a.Priority));
    }
}
▶ 試してみよう

4. ファイルへの永続化:JSONのシリアライズとデシリアライズ

System.Text.Jsonを使用すると、タスクリストをファイルに保存したり、ファイルから読み込んだりできます。

▶ サンプル

CSHARP 📖 参照専用
using System.Text.Json;

class TaskManager
{
    private List<Task> tasks = new List<Task>();
    private int nextId = 1;
    private const string FilePath = "tasks.json";
    private static readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions
    {
        WriteIndented = true
    };

    public string SaveToFile()
    {
        try
        {
            string json = JsonSerializer.Serialize(tasks, jsonOptions);
            File.WriteAllText(FilePath, json);
            return "Save succeeded.";
        }
        catch (Exception ex)
        {
            return $"Save failed: {ex.Message}";
        }
    }

    public string LoadFromFile()
    {
        try
        {
            if (!File.Exists(FilePath))
            {
                return "File does not exist, skipping load.";
            }
            string json = File.ReadAllText(FilePath);
            List<Task>? loaded = JsonSerializer.Deserialize<List<Task>>(json);
            if (loaded != null)
            {
                tasks = loaded;
                nextId = tasks.Count > 0 ? tasks.Max(t => t.Id) + 1 : 1;
            }
            return "Load succeeded.";
        }
        catch (JsonException ex)
        {
            return $"JSON format error: {ex.Message}";
        }
        catch (Exception ex)
        {
            return $"Load failed: {ex.Message}";
        }
    }
}
論理コード 50 行(40 行制限超過、参照専用)

5. LINQ クエリ:フィルタリングと集計

LINQ を使用して、優先度によるフィルタリング、保留中のタスクの照会、および統計情報の生成を実装します。

▶ サンプル

CSHARP
class TaskManager
{
    private List<Task> tasks = new List<Task>();

    public List<Task> FilterByPriority(Priority priority)
    {
        return tasks.Where(t => t.Priority == priority).ToList();
    }

    public List<Task> GetPendingTasks()
    {
        return tasks.Where(t => !t.IsCompleted)
                    .OrderByDescending(t => t.Priority)
                    .ToList();
    }

    public Dictionary<Priority, int> GetStatistics()
    {
        return tasks.GroupBy(t => t.Priority)
                    .ToDictionary(g => g.Key, g => g.Count());
    }

    public int GetCompletedCount()
    {
        return tasks.Count(t => t.IsCompleted);
    }

    public int GetPendingCount()
    {
        return tasks.Count(t => !t.IsCompleted);
    }
}
▶ 試してみよう
TEXT 📖 参照専用
Sample statistics output:
High: 2, Medium: 3, Low: 1
Completed: 1, Pending: 5

6. 非同期でのファイル保存

async/awaitを使用して、ノンブロッキングのファイル書き込み操作を実装します。

▶ サンプル

CSHARP 📖 参照専用
class TaskManager
{
    private List<Task> tasks = new List<Task>();
    private const string FilePath = "tasks.json";
    private static readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions
    {
        WriteIndented = true
    };

    public async Task````<string>```` SaveToFileAsync()
    {
        try
        {
            string json = JsonSerializer.Serialize(tasks, jsonOptions);
            await File.WriteAllTextAsync(FilePath, json);
            return "Async save succeeded.";
        }
        catch (UnauthorizedAccessException ex)
        {
            return $"File access denied: {ex.Message}";
        }
        catch (IOException ex)
        {
            return $"IO error: {ex.Message}";
        }
        catch (Exception ex)
        {
            return $"Async save failed: {ex.Message}";
        }
    }

    public async Task````<string>```` LoadFromFileAsync()
    {
        try
        {
            if (!File.Exists(FilePath))
            {
                return "File does not exist, skipping load.";
            }
            string json = await File.ReadAllTextAsync(FilePath);
            List<Task>? loaded = JsonSerializer.Deserialize<List<Task>>(json);
            if (loaded != null)
            {
                tasks = loaded;
                nextId = tasks.Count > 0 ? tasks.Max(t => t.Id) + 1 : 1;
            }
            return "Async load succeeded.";
        }
        catch (JsonException ex)
        {
            return $"JSON format error: {ex.Message}";
        }
        catch (Exception ex)
        {
            return $"Async load failed: {ex.Message}";
        }
    }
}
論理コード 56 行(40 行制限超過、参照専用)

7. コマンドライン対話型インターフェース

switchステートメントを使用して、コマンドラインメニューのループを構築します。

▶ サンプル

CSHARP 📖 参照専用
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        TaskManager manager = new TaskManager();
        string loadResult = await manager.LoadFromFileAsync();
        Console.WriteLine(loadResult);

        while (true)
        {
            Console.WriteLine("\n===== Task Manager =====");
            Console.WriteLine("1. Add task");
            Console.WriteLine("2. Delete task");
            Console.WriteLine("3. Complete task");
            Console.WriteLine("4. List all tasks");
            Console.WriteLine("5. Filter by priority");
            Console.WriteLine("6. View statistics");
            Console.WriteLine("7. Save to file");
            Console.WriteLine("0. Exit");
            Console.Write("Select: ");
            string? input = Console.ReadLine();

            switch (input)
            {
                case "1":
                    Console.Write("Title: ");
                    string? title = Console.ReadLine() ?? "";
                    Console.Write("Description: ");
                    string? desc = Console.ReadLine() ?? "";
                    Console.Write("Priority (Low/Medium/High): ");
                    string? priStr = Console.ReadLine();
                    if (Enum.TryParse<Priority>(priStr, true, out Priority pri))
                    {
                        Task added = manager.AddTask(title, desc, pri);
                        Console.WriteLine($"Added: {added}");
                    }
                    else
                    {
                        Console.WriteLine("Invalid priority.");
                    }
                    break;
                case "2":
                    Console.Write("Task ID: ");
                    if (int.TryParse(Console.ReadLine(), out int delId))
                    {
                        Console.WriteLine(manager.RemoveTask(delId) ? "Deleted." : "Not found.");
                    }
                    break;
                case "3":
                    Console.Write("Task ID: ");
                    if (int.TryParse(Console.ReadLine(), out int compId))
                    {
                        Console.WriteLine(manager.CompleteTask(compId) ? "Completed." : "Not found.");
                    }
                    break;
                case "4":
                    manager.SortByPriority();
                    foreach (Task t in manager.GetAllTasks())
                    {
                        Console.WriteLine(t);
                    }
                    break;
                case "5":
                    Console.Write("Priority (Low/Medium/High): ");
                    if (Enum.TryParse<Priority>(Console.ReadLine(), true, out Priority filterPri))
                    {
                        foreach (Task t in manager.FilterByPriority(filterPri))
                        {
                            Console.WriteLine(t);
                        }
                    }
                    break;
                case "6":
                    Dictionary<Priority, int> stats = manager.GetStatistics();
                    foreach (var kv in stats)
                    {
                        Console.WriteLine($"{kv.Key}: {kv.Value}");
                    }
                    Console.WriteLine($"Completed: {manager.GetCompletedCount()}, Pending: {manager.GetPendingCount()}");
                    break;
                case "7":
                    string saveResult = await manager.SaveToFileAsync();
                    Console.WriteLine(saveResult);
                    break;
                case "0":
                    string finalSave = await manager.SaveToFileAsync();
                    Console.WriteLine(finalSave);
                    Console.WriteLine("Goodbye!");
                    return;
                default:
                    Console.WriteLine("Invalid selection.");
                    break;
            }
        }
    }
}
論理コード 96 行(40 行制限超過、参照専用)
TEXT 📖 参照専用
===== Task Manager =====
1. Add task
2. Delete task
3. Complete task
4. List all tasks
5. Filter by priority
6. View statistics
7. Save to file
0. Exit
Select: 1
Title: Learn C#
Description: Complete lesson 36
Priority (Low/Medium/High): High
Added: 1   [ ] High   2026-06-28 Learn C#

8. プロジェクト体制の構築

名前空間とusingを使用してコードを整理し、異なる役割を別々のファイルに配置します。

▶ サンプル

プロジェクトのファイル構成:

TEXT 📖 参照専用
TaskManagerApp/
├── Models/
│   ├── Priority.cs
│   └── Task.cs
├── Services/
│   └── TaskManager.cs
└── Program.cs

Priority.cs:

CSHARP
namespace TaskManagerApp.Models
{
    public enum Priority
    {
        Low,
        Medium,
        High
    }
}

Task.cs:

CSHARP
using System;

namespace TaskManagerApp.Models
{
    public class Task
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public Priority Priority { get; set; }
        public bool IsCompleted { get; set; }
        public DateTime CreatedAt { get; set; }

        public Task(int id, string title, string description, Priority priority)
        {
            Id = id;
            Title = title;
            Description = description;
            Priority = priority;
            IsCompleted = false;
            CreatedAt = DateTime.Now;
        }

        public override string ToString()
        {
            string status = IsCompleted ? "[x]" : "[ ]";
            return $"{Id,-3} {status} {Priority,-6} {CreatedAt:yyyy-MM-dd} {Title}";
        }
    }
}

TaskManager.cs:

CSHARP
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using TaskManagerApp.Models;

namespace TaskManagerApp.Services
{
    public class TaskManager
    {
        private List<Models.Task> tasks = new List<Models.Task>();
        private int nextId = 1;
        private const string FilePath = "tasks.json";
        private static readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions
        {
            WriteIndented = true
        };

        public Models.Task AddTask(string title, string description, Priority priority)
        {
            Models.Task task = new Models.Task(nextId++, title, description, priority);
            tasks.Add(task);
            return task;
        }

        public bool RemoveTask(int id)
        {
            Models.Task? task = tasks.Find(t => t.Id == id);
            if (task != null)
            {
                tasks.Remove(task);
                return true;
            }
            return false;
        }

        public bool CompleteTask(int id)
        {
            Models.Task? task = tasks.Find(t => t.Id == id);
            if (task != null)
            {
                task.IsCompleted = true;
                return true;
            }
            return false;
        }

        public List<Models.Task> GetAllTasks()
        {
            return new List<Models.Task>(tasks);
        }

        public void SortByPriority()
        {
            tasks.Sort((a, b) => b.Priority.CompareTo(a.Priority));
        }

        public List<Models.Task> FilterByPriority(Priority priority)
        {
            return tasks.Where(t => t.Priority == priority).ToList();
        }

        public List<Models.Task> GetPendingTasks()
        {
            return tasks.Where(t => !t.IsCompleted)
                        .OrderByDescending(t => t.Priority)
                        .ToList();
        }

        public Dictionary<Priority, int> GetStatistics()
        {
            return tasks.GroupBy(t => t.Priority)
                        .ToDictionary(g => g.Key, g => g.Count());
        }

        public int GetCompletedCount()
        {
            return tasks.Count(t => t.IsCompleted);
        }

        public int GetPendingCount()
        {
            return tasks.Count(t => !t.IsCompleted);
        }

        public async Task````<string>```` SaveToFileAsync()
        {
            try
            {
                string json = JsonSerializer.Serialize(tasks, jsonOptions);
                await File.WriteAllTextAsync(FilePath, json);
                return "Save succeeded.";
            }
            catch (UnauthorizedAccessException ex)
            {
                return $"Access denied: {ex.Message}";
            }
            catch (IOException ex)
            {
                return $"IO error: {ex.Message}";
            }
            catch (Exception ex)
            {
                return $"Save failed: {ex.Message}";
            }
        }

        public async Task````<string>```` LoadFromFileAsync()
        {
            try
            {
                if (!File.Exists(FilePath))
                {
                    return "File does not exist, skipping load.";
                }
                string json = await File.ReadAllTextAsync(FilePath);
                List<Models.Task>? loaded = JsonSerializer.Deserialize<List<Models.Task>>(json);
                if (loaded != null)
                {
                    tasks = loaded;
                    nextId = tasks.Count > 0 ? tasks.Max(t => t.Id) + 1 : 1;
                }
                return "Load succeeded.";
            }
            catch (JsonException ex)
            {
                return $"JSON format error: {ex.Message}";
            }
            catch (Exception ex)
            {
                return $"Load failed: {ex.Message}";
            }
        }
    }
}

Program.cs:

CSHARP
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TaskManagerApp.Models;
using TaskManagerApp.Services;

namespace TaskManagerApp
{
    class Program
    {
        static async Task Main()
        {
            TaskManager manager = new TaskManager();
            string loadResult = await manager.LoadFromFileAsync();
            Console.WriteLine(loadResult);

            while (true)
            {
                Console.WriteLine("\n===== Task Manager =====");
                Console.WriteLine("1. Add task");
                Console.WriteLine("2. Delete task");
                Console.WriteLine("3. Complete task");
                Console.WriteLine("4. List all tasks");
                Console.WriteLine("5. Filter by priority");
                Console.WriteLine("6. View statistics");
                Console.WriteLine("7. Save to file");
                Console.WriteLine("0. Exit");
                Console.Write("Select: ");
                string? input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        Console.Write("Title: ");
                        string? title = Console.ReadLine() ?? "";
                        Console.Write("Description: ");
                        string? desc = Console.ReadLine() ?? "";
                        Console.Write("Priority (Low/Medium/High): ");
                        string? priStr = Console.ReadLine();
                        if (Enum.TryParse<Priority>(priStr, true, out Priority pri))
                        {
                            Models.Task added = manager.AddTask(title, desc, pri);
                            Console.WriteLine($"Added: {added}");
                        }
                        else
                        {
                            Console.WriteLine("Invalid priority.");
                        }
                        break;
                    case "2":
                        Console.Write("Task ID: ");
                        if (int.TryParse(Console.ReadLine(), out int delId))
                        {
                            Console.WriteLine(manager.RemoveTask(delId) ? "Deleted." : "Not found.");
                        }
                        break;
                    case "3":
                        Console.Write("Task ID: ");
                        if (int.TryParse(Console.ReadLine(), out int compId))
                        {
                            Console.WriteLine(manager.CompleteTask(compId) ? "Completed." : "Not found.");
                        }
                        break;
                    case "4":
                        manager.SortByPriority();
                        foreach (Models.Task t in manager.GetAllTasks())
                        {
                            Console.WriteLine(t);
                        }
                        break;
                    case "5":
                        Console.Write("Priority (Low/Medium/High): ");
                        if (Enum.TryParse<Priority>(Console.ReadLine(), true, out Priority filterPri))
                        {
                            foreach (Models.Task t in manager.FilterByPriority(filterPri))
                            {
                                Console.WriteLine(t);
                            }
                        }
                        break;
                    case "6":
                        Dictionary<Priority, int> stats = manager.GetStatistics();
                        foreach (var kv in stats)
                        {
                            Console.WriteLine($"{kv.Key}: {kv.Value}");
                        }
                        Console.WriteLine($"Completed: {manager.GetCompletedCount()}, Pending: {manager.GetPendingCount()}");
                        break;
                    case "7":
                        string saveResult = await manager.SaveToFileAsync();
                        Console.WriteLine(saveResult);
                        break;
                    case "0":
                        string finalSave = await manager.SaveToFileAsync();
                        Console.WriteLine(finalSave);
                        Console.WriteLine("Goodbye!");
                        return;
                    default:
                        Console.WriteLine("Invalid selection.");
                        break;
                }
            }
        }
    }
}

9. 完全なシングルファイル版

迅速なテストを行うために、すべてのコードを1つのコンパイル可能なファイルに統合してください。

▶ サンプル

CSHARP 📖 参照専用
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

enum Priority
{
    Low,
    Medium,
    High
}

class Task
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Priority Priority { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }

    public Task(int id, string title, string description, Priority priority)
    {
        Id = id;
        Title = title;
        Description = description;
        Priority = priority;
        IsCompleted = false;
        CreatedAt = DateTime.Now;
    }

    public override string ToString()
    {
        string status = IsCompleted ? "[x]" : "[ ]";
        return $"{Id,-3} {status} {Priority,-6} {CreatedAt:yyyy-MM-dd} {Title}";
    }
}

class TaskManager
{
    private List<Task> tasks = new List<Task>();
    private int nextId = 1;
    private const string FilePath = "tasks.json";
    private static readonly JsonSerializerOptions jsonOptions = new JsonSerializerOptions
    {
        WriteIndented = true
    };

    public Task AddTask(string title, string description, Priority priority)
    {
        Task task = new Task(nextId++, title, description, priority);
        tasks.Add(task);
        return task;
    }

    public bool RemoveTask(int id)
    {
        Task? task = tasks.Find(t => t.Id == id);
        if (task != null)
        {
            tasks.Remove(task);
            return true;
        }
        return false;
    }

    public bool CompleteTask(int id)
    {
        Task? task = tasks.Find(t => t.Id == id);
        if (task != null)
        {
            task.IsCompleted = true;
            return true;
        }
        return false;
    }

    public List<Task> GetAllTasks()
    {
        return new List<Task>(tasks);
    }

    public void SortByPriority()
    {
        tasks.Sort((a, b) => b.Priority.CompareTo(a.Priority));
    }

    public List<Task> FilterByPriority(Priority priority)
    {
        return tasks.Where(t => t.Priority == priority).ToList();
    }

    public List<Task> GetPendingTasks()
    {
        return tasks.Where(t => !t.IsCompleted)
                    .OrderByDescending(t => t.Priority)
                    .ToList();
    }

    public Dictionary<Priority, int> GetStatistics()
    {
        return tasks.GroupBy(t => t.Priority)
                    .ToDictionary(g => g.Key, g => g.Count());
    }

    public int GetCompletedCount()
    {
        return tasks.Count(t => t.IsCompleted);
    }

    public int GetPendingCount()
    {
        return tasks.Count(t => !t.IsCompleted);
    }

    public async Task````<string>```` SaveToFileAsync()
    {
        try
        {
            string json = JsonSerializer.Serialize(tasks, jsonOptions);
            await File.WriteAllTextAsync(FilePath, json);
            return "Save succeeded.";
        }
        catch (UnauthorizedAccessException ex)
        {
            return $"Access denied: {ex.Message}";
        }
        catch (IOException ex)
        {
            return $"IO error: {ex.Message}";
        }
        catch (Exception ex)
        {
            return $"Save failed: {ex.Message}";
        }
    }

    public async Task````<string>```` LoadFromFileAsync()
    {
        try
        {
            if (!File.Exists(FilePath))
            {
                return "File does not exist, skipping load.";
            }
            string json = await File.ReadAllTextAsync(FilePath);
            List<Task>? loaded = JsonSerializer.Deserialize<List<Task>>(json);
            if (loaded != null)
            {
                tasks = loaded;
                nextId = tasks.Count > 0 ? tasks.Max(t => t.Id) + 1 : 1;
            }
            return "Load succeeded.";
        }
        catch (JsonException ex)
        {
            return $"JSON format error: {ex.Message}";
        }
        catch (Exception ex)
        {
            return $"Load failed: {ex.Message}";
        }
    }
}

class Program
{
    static async Task Main()
    {
        TaskManager manager = new TaskManager();
        string loadResult = await manager.LoadFromFileAsync();
        Console.WriteLine(loadResult);

        while (true)
        {
            Console.WriteLine("\n===== Task Manager =====");
            Console.WriteLine("1. Add task");
            Console.WriteLine("2. Delete task");
            Console.WriteLine("3. Complete task");
            Console.WriteLine("4. List all tasks");
            Console.WriteLine("5. Filter by priority");
            Console.WriteLine("6. View statistics");
            Console.WriteLine("7. Save to file");
            Console.WriteLine("0. Exit");
            Console.Write("Select: ");
            string? input = Console.ReadLine();

            switch (input)
            {
                case "1":
                    Console.Write("Title: ");
                    string? title = Console.ReadLine() ?? "";
                    Console.Write("Description: ");
                    string? desc = Console.ReadLine() ?? "";
                    Console.Write("Priority (Low/Medium/High): ");
                    string? priStr = Console.ReadLine();
                    if (Enum.TryParse<Priority>(priStr, true, out Priority pri))
                    {
                        Task added = manager.AddTask(title, desc, pri);
                        Console.WriteLine($"Added: {added}");
                    }
                    else
                    {
                        Console.WriteLine("Invalid priority.");
                    }
                    break;
                case "2":
                    Console.Write("Task ID: ");
                    if (int.TryParse(Console.ReadLine(), out int delId))
                    {
                        Console.WriteLine(manager.RemoveTask(delId) ? "Deleted." : "Not found.");
                    }
                    break;
                case "3":
                    Console.Write("Task ID: ");
                    if (int.TryParse(Console.ReadLine(), out int compId))
                    {
                        Console.WriteLine(manager.CompleteTask(compId) ? "Completed." : "Not found.");
                    }
                    break;
                case "4":
                    manager.SortByPriority();
                    foreach (Task t in manager.GetAllTasks())
                    {
                        Console.WriteLine(t);
                    }
                    break;
                case "5":
                    Console.Write("Priority (Low/Medium/High): ");
                    if (Enum.TryParse<Priority>(Console.ReadLine(), true, out Priority filterPri))
                    {
                        foreach (Task t in manager.FilterByPriority(filterPri))
                        {
                            Console.WriteLine(t);
                        }
                    }
                    break;
                case "6":
                    Dictionary<Priority, int> stats = manager.GetStatistics();
                    foreach (var kv in stats)
                    {
                        Console.WriteLine($"{kv.Key}: {kv.Value}");
                    }
                    Console.WriteLine($"Completed: {manager.GetCompletedCount()}, Pending: {manager.GetPendingCount()}");
                    break;
                case "7":
                    string saveResult = await manager.SaveToFileAsync();
                    Console.WriteLine(saveResult);
                    break;
                case "0":
                    string finalSave = await manager.SaveToFileAsync();
                    Console.WriteLine(finalSave);
                    Console.WriteLine("Goodbye!");
                    return;
                default:
                    Console.WriteLine("Invalid selection.");
                    break;
            }
        }
    }
}
論理コード 243 行(40 行制限超過、参照専用)
TEXT 📖 参照専用
Load succeeded.

===== Task Manager =====
1. Add task
2. Delete task
3. Complete task
4. List all tasks
5. Filter by priority
6. View statistics
7. Save to file
0. Exit
Select: 1
Title: Learn C#
Description: Complete lesson 36
Priority (Low/Medium/High): High
Added: 1   [ ] High   2026-06-28 Learn C#

Select: 1
Title: Write documentation
Description: Organize project docs
Priority (Low/Medium/High): Medium
Added: 2   [ ] Medium 2026-06-28 Write documentation

Select: 3
Task ID: 2
Completed.

Select: 4
2   [x] Medium 2026-06-28 Write documentation
1   [ ] High   2026-06-28 Learn C#

Select: 6
High: 1
Medium: 1
Completed: 1, Pending: 1

Select: 0
Save succeeded.
Goodbye!

❓ よくある質問

Q シリアライズにはなぜパラメータなしのコンストラクタが必要なのですか?
A System.Text.Json ではデフォルトでパラメータなしのコンストラクタが必須となっています。JsonSerializerOptions を使用してコンバータをカスタマイズすることで、この要件を回避することができます。
Q List.Find で要素が見つからなかった場合はどうなりますか?
A Find は例外をスローすることなく null を返します。結果を使用する前に、必ず null チェックを行ってください。
Q 非同期メソッドに await が含まれていない場合はどうなりますか?
A そのタスクは「ファイア・アンド・フォーゲット」状態となり、例外が捕捉されず、データ損失につながる可能性があります。
Q Enum.TryParse は失敗した場合、何を返しますか?
A false を返し、出力パラメータはデフォルト値のままとなり、例外はスローされません。

📖 まとめ

📝 練習問題

  1. Task クラスに DueDate プロパティを追加し、「期限切れのタスクを表示する」機能を実装する
  2. TaskManager に、タイトルと説明の変更をサポートする EditTask メソッドを追加する
  3. 保存形式をJSONからCSVに変更し、独自のシリアライズロジックを実装する
  4. 作成日による並べ替えとタイトルによる検索機能を追加する
  5. コマンドライン引数モードを実装する:プログラムは起動時に引数に基づいて操作を実行し、その後終了する
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%