综合项目:任务管理器
需求分析
我们要构建一个命令行任务管理器,支持以下功能:
- 添加、删除、完成任务
- 按优先级筛选和统计
- 数据持久化到JSON文件
- 异步保存,不阻塞主流程
- 完善的异常处理
功能拆解为七个步骤,逐步实现。
类设计:枚举与数据模型
首先定义优先级枚举和任务实体类。
示例
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}";
}
}
集合存储: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));
}
}
文件持久化: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 "保存成功。";
}
catch (Exception ex)
{
return $"保存失败:{ex.Message}";
}
}
public string LoadFromFile()
{
try
{
if (!File.Exists(FilePath))
{
return "文件不存在,跳过加载。";
}
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 "加载成功。";
}
catch (JsonException ex)
{
return $"JSON格式错误:{ex.Message}";
}
catch (Exception ex)
{
return $"加载失败:{ex.Message}";
}
}
}
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
统计输出示例:
High: 2, Medium: 3, Low: 1
已完成: 1, 未完成: 5
异步文件保存
使用 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 "异步保存成功。";
}
catch (UnauthorizedAccessException ex)
{
return $"无权限访问文件:{ex.Message}";
}
catch (IOException ex)
{
return $"IO错误:{ex.Message}";
}
catch (Exception ex)
{
return $"异步保存失败:{ex.Message}";
}
}
public async Task<string> LoadFromFileAsync()
{
try
{
if (!File.Exists(FilePath))
{
return "文件不存在,跳过加载。";
}
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 "异步加载成功。";
}
catch (JsonException ex)
{
return $"JSON格式错误:{ex.Message}";
}
catch (Exception ex)
{
return $"异步加载失败:{ex.Message}";
}
}
}
命令行交互界面
使用 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===== 任务管理器 =====");
Console.WriteLine("1. 添加任务");
Console.WriteLine("2. 删除任务");
Console.WriteLine("3. 完成任务");
Console.WriteLine("4. 列出所有任务");
Console.WriteLine("5. 按优先级筛选");
Console.WriteLine("6. 查看统计");
Console.WriteLine("7. 保存到文件");
Console.WriteLine("0. 退出");
Console.Write("请选择:");
string? input = Console.ReadLine();
switch (input)
{
case "1":
Console.Write("标题:");
string? title = Console.ReadLine() ?? "";
Console.Write("描述:");
string? desc = Console.ReadLine() ?? "";
Console.Write("优先级(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}");
}
else
{
Console.WriteLine("无效优先级。");
}
break;
case "2":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int delId))
{
Console.WriteLine(manager.RemoveTask(delId) ? "已删除。" : "未找到。");
}
break;
case "3":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int compId))
{
Console.WriteLine(manager.CompleteTask(compId) ? "已完成。" : "未找到。");
}
break;
case "4":
manager.SortByPriority();
foreach (Task t in manager.GetAllTasks())
{
Console.WriteLine(t);
}
break;
case "5":
Console.Write("优先级(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($"已完成: {manager.GetCompletedCount()}, 未完成: {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("再见!");
return;
default:
Console.WriteLine("无效选择。");
break;
}
}
}
}
TEXT
===== 任务管理器 =====
1. 添加任务
2. 删除任务
3. 完成任务
4. 列出所有任务
5. 按优先级筛选
6. 查看统计
7. 保存到文件
0. 退出
请选择:1
标题:学习C#
描述:完成第36课
优先级(Low/Medium/High):High
已添加:1 [ ] High 2026-06-28 学习C#
项目结构组织
使用命名空间和 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 "保存成功。";
}
catch (UnauthorizedAccessException ex)
{
return $"无权限:{ex.Message}";
}
catch (IOException ex)
{
return $"IO错误:{ex.Message}";
}
catch (Exception ex)
{
return $"保存失败:{ex.Message}";
}
}
public async Task<string> LoadFromFileAsync()
{
try
{
if (!File.Exists(FilePath))
{
return "文件不存在,跳过加载。";
}
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 "加载成功。";
}
catch (JsonException ex)
{
return $"JSON格式错误:{ex.Message}";
}
catch (Exception ex)
{
return $"加载失败:{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===== 任务管理器 =====");
Console.WriteLine("1. 添加任务");
Console.WriteLine("2. 删除任务");
Console.WriteLine("3. 完成任务");
Console.WriteLine("4. 列出所有任务");
Console.WriteLine("5. 按优先级筛选");
Console.WriteLine("6. 查看统计");
Console.WriteLine("7. 保存到文件");
Console.WriteLine("0. 退出");
Console.Write("请选择:");
string? input = Console.ReadLine();
switch (input)
{
case "1":
Console.Write("标题:");
string? title = Console.ReadLine() ?? "";
Console.Write("描述:");
string? desc = Console.ReadLine() ?? "";
Console.Write("优先级(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}");
}
else
{
Console.WriteLine("无效优先级。");
}
break;
case "2":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int delId))
{
Console.WriteLine(manager.RemoveTask(delId) ? "已删除。" : "未找到。");
}
break;
case "3":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int compId))
{
Console.WriteLine(manager.CompleteTask(compId) ? "已完成。" : "未找到。");
}
break;
case "4":
manager.SortByPriority();
foreach (Models.Task t in manager.GetAllTasks())
{
Console.WriteLine(t);
}
break;
case "5":
Console.Write("优先级(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($"已完成: {manager.GetCompletedCount()}, 未完成: {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("再见!");
return;
default:
Console.WriteLine("无效选择。");
break;
}
}
}
}
}
完整单文件版本
将所有代码合并为一个可编译运行的文件,方便快速测试。
示例
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 "保存成功。";
}
catch (UnauthorizedAccessException ex)
{
return $"无权限:{ex.Message}";
}
catch (IOException ex)
{
return $"IO错误:{ex.Message}";
}
catch (Exception ex)
{
return $"保存失败:{ex.Message}";
}
}
public async Task<string> LoadFromFileAsync()
{
try
{
if (!File.Exists(FilePath))
{
return "文件不存在,跳过加载。";
}
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 "加载成功。";
}
catch (JsonException ex)
{
return $"JSON格式错误:{ex.Message}";
}
catch (Exception ex)
{
return $"加载失败:{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===== 任务管理器 =====");
Console.WriteLine("1. 添加任务");
Console.WriteLine("2. 删除任务");
Console.WriteLine("3. 完成任务");
Console.WriteLine("4. 列出所有任务");
Console.WriteLine("5. 按优先级筛选");
Console.WriteLine("6. 查看统计");
Console.WriteLine("7. 保存到文件");
Console.WriteLine("0. 退出");
Console.Write("请选择:");
string? input = Console.ReadLine();
switch (input)
{
case "1":
Console.Write("标题:");
string? title = Console.ReadLine() ?? "";
Console.Write("描述:");
string? desc = Console.ReadLine() ?? "";
Console.Write("优先级(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}");
}
else
{
Console.WriteLine("无效优先级。");
}
break;
case "2":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int delId))
{
Console.WriteLine(manager.RemoveTask(delId) ? "已删除。" : "未找到。");
}
break;
case "3":
Console.Write("任务ID:");
if (int.TryParse(Console.ReadLine(), out int compId))
{
Console.WriteLine(manager.CompleteTask(compId) ? "已完成。" : "未找到。");
}
break;
case "4":
manager.SortByPriority();
foreach (Task t in manager.GetAllTasks())
{
Console.WriteLine(t);
}
break;
case "5":
Console.Write("优先级(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($"已完成: {manager.GetCompletedCount()}, 未完成: {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("再见!");
return;
default:
Console.WriteLine("无效选择。");
break;
}
}
}
}
TEXT
加载成功。
===== 任务管理器 =====
1. 添加任务
2. 删除任务
3. 完成任务
4. 列出所有任务
5. 按优先级筛选
6. 查看统计
7. 保存到文件
0. 退出
请选择:1
标题:学习C#
描述:完成第36课
优先级(Low/Medium/High):High
已添加:1 [ ] High 2026-06-28 学习C#
请选择:1
标题:写文档
描述:整理项目文档
优先级(Low/Medium/High):Medium
已添加:2 [ ] Medium 2026-06-28 写文档
请选择:3
任务ID:2
已完成。
请选择:4
2 [x] Medium 2026-06-28 写文档
1 [ ] High 2026-06-28 学习C#
请选择:6
High: 1
Medium: 1
已完成: 1, 未完成: 1
请选择:0
保存成功。
再见!
❓ 常见问题
Q 为什么序列化时需要无参构造函数?
A System.Text.Json默认要求无参构造函数,可通过JsonSerializerOptions自定义转换器绕过。
Q List.Find找不到元素会怎样?
A Find返回null,不会抛异常,使用前必须判空。
Q 异步方法不加await会怎样?
A 任务会"发射后不管",异常不会被捕获,可能导致数据丢失。
Q Enum.TryParse失败返回什么?
A 返回false,输出参数保持默认值,不会抛异常。
📖 小节
- 枚举类型定义有限状态集合,提高代码可读性
- 实体类封装数据与行为,ToString提供格式化输出
- List
搭配Find/Sort/Where实现集合操作 - System.Text.Json实现轻量级序列化与反序列化
- LINQ的Where/OrderBy/GroupBy实现声明式查询
- async/await实现非阻塞文件IO
- try-catch分层捕获特定异常再兜底Exception
- switch语句构建命令行菜单循环
- namespace和using组织多文件项目结构
📝 作业
- 为Task类添加DueDate属性,实现"显示已过期任务"功能
- 为TaskManager添加EditTask方法,支持修改标题和描述
- 将保存格式从JSON改为CSV,自行实现序列化逻辑
- 添加按创建日期排序和按标题搜索功能
- 实现命令行参数模式:程序启动时通过args直接执行操作并退出



