项目2 - 待办事项管理
嗨,朋友!我是长安。
完成了计算器项目,这次我们要做一个待办事项管理应用,学习如何使用集合管理数据,并实现数据的持久化存储。这个项目会帮你掌握面向对象编程、集合操作和文件读写。
🎯 项目需求
功能列表
- 添加待办事项
- 查看所有待办事项(区分已完成和未完成)
- 标记待办事项为完成
- 删除待办事项
- 保存到文件/从文件加载
- 退出程序
运行效果
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 保存并退出
请选择操作: 1
请输入待办事项: 学习 C# 面向对象编程
✓ 添加成功!
请选择操作: 2
===== 待办事项列表 =====
[ ] 1. 学习 C# 面向对象编程
===========================
🤔 需求分析
在开始写代码之前,我们先分析一下需要做什么:
数据管理
- 定义待办事项的数据结构
- 使用集合存储多个待办事项
- 支持增删改查操作
用户交互
- 显示菜单供用户选择
- 接收用户输入并执行相应操作
- 提供友好的操作反馈
数据持久化
- 将数据保存到文件
- 程序启动时从文件加载数据
- 确保数据不丢失
异常处理
- 处理无效输入
- 处理文件读写异常
📐 功能设计
1. 程序流程图
开始
↓
从文件加载数据
↓
┌─────────────┐
│ 显示菜单 │←────┐
└─────────────┘ │
↓ │
获取用户选择 │
↓ │
┌─────────────┐ │
│ 1.添加事项 │─────┤
│ 2.查看事项 │─────┤
│ 3.标记完成 │─────┤
│ 4.删除事项 │─────┤
│ 5.保存退出 │─────┘
└─────────────┘
↓
保存数据到文件
↓
结束
2. 类设计
TodoItem 类 - 表示单个待办事项
- 属性:Id、内容、是否完成、创建时间
TodoManager 类 - 管理所有待办事项
- 方法:添加、删除、标记完成、查看、保存、加载
💻 代码实现
第一步:创建项目
# 创建新的控制台项目
mkdir TodoListApp
cd TodoListApp
dotnet new console
第二步:定义 TodoItem 类
首先,我们需要定义待办事项的数据结构:
using System;
namespace TodoListApp
{
/// <summary>
/// 待办事项类
/// </summary>
public class TodoItem
{
// 唯一标识符
public int Id { get; set; }
// 事项内容
public string Content { get; set; }
// 是否完成
public bool IsCompleted { get; set; }
// 创建时间
public DateTime CreatedTime { get; set; }
// 构造函数
public TodoItem(int id, string content)
{
Id = id;
Content = content;
IsCompleted = false;
CreatedTime = DateTime.Now;
}
// 重载 ToString 方法,方便显示
public override string ToString()
{
string status = IsCompleted ? "✓" : " ";
return $"[{status}] {Id}. {Content}";
}
}
}
代码讲解:
Id:每个待办事项的唯一编号,便于查找和操作Content:事项的具体内容IsCompleted:标记事项是否已完成,默认为 falseCreatedTime:记录创建时间,可用于排序ToString():重写该方法,让对象显示时更友好
第三步:创建 TodoManager 管理类
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TodoListApp
{
/// <summary>
/// 待办事项管理类
/// </summary>
public class TodoManager
{
private List<TodoItem> todoItems; // 存储所有待办事项
private int nextId; // 下一个可用的 ID
private readonly string filePath; // 数据文件路径
// 构造函数
public TodoManager(string filePath = "todos.txt")
{
this.filePath = filePath;
todoItems = new List<TodoItem>();
nextId = 1;
LoadFromFile();
}
/// <summary>
/// 添加待办事项
/// </summary>
public void AddTodo(string content)
{
if (string.IsNullOrWhiteSpace(content))
{
Console.WriteLine("✗ 内容不能为空!");
return;
}
var todo = new TodoItem(nextId++, content);
todoItems.Add(todo);
Console.WriteLine("✓ 添加成功!");
}
/// <summary>
/// 显示所有待办事项
/// </summary>
public void ShowAll()
{
Console.WriteLine("\n===== 待办事项列表 =====");
if (todoItems.Count == 0)
{
Console.WriteLine("暂无待办事项");
}
else
{
// 按完成状态分组显示
var uncompletedItems = todoItems.Where(t => !t.IsCompleted).ToList();
var completedItems = todoItems.Where(t => t.IsCompleted).ToList();
if (uncompletedItems.Count > 0)
{
Console.WriteLine("\n未完成:");
foreach (var item in uncompletedItems)
{
Console.WriteLine(item);
}
}
if (completedItems.Count > 0)
{
Console.WriteLine("\n已完成:");
Console.ForegroundColor = ConsoleColor.Gray;
foreach (var item in completedItems)
{
Console.WriteLine(item);
}
Console.ResetColor();
}
}
Console.WriteLine("===========================");
}
/// <summary>
/// 标记待办事项为完成
/// </summary>
public void MarkAsCompleted(int id)
{
var todo = todoItems.FirstOrDefault(t => t.Id == id);
if (todo == null)
{
Console.WriteLine("✗ 找不到该事项!");
return;
}
if (todo.IsCompleted)
{
Console.WriteLine("该事项已经完成了!");
return;
}
todo.IsCompleted = true;
Console.WriteLine("✓ 已标记为完成!");
}
/// <summary>
/// 删除待办事项
/// </summary>
public void DeleteTodo(int id)
{
var todo = todoItems.FirstOrDefault(t => t.Id == id);
if (todo == null)
{
Console.WriteLine("✗ 找不到该事项!");
return;
}
todoItems.Remove(todo);
Console.WriteLine("✓ 删除成功!");
}
/// <summary>
/// 保存到文件
/// </summary>
public void SaveToFile()
{
try
{
using (StreamWriter writer = new StreamWriter(filePath))
{
// 保存下一个可用的 ID
writer.WriteLine(nextId);
// 保存每个待办事项
foreach (var item in todoItems)
{
string line = $"{item.Id}|{item.Content}|{item.IsCompleted}|{item.CreatedTime}";
writer.WriteLine(line);
}
}
Console.WriteLine("\n✓ 数据已保存!");
}
catch (Exception ex)
{
Console.WriteLine($"\n✗ 保存失败: {ex.Message}");
}
}
/// <summary>
/// 从文件加载
/// </summary>
private void LoadFromFile()
{
if (!File.Exists(filePath))
{
return;
}
try
{
using (StreamReader reader = new StreamReader(filePath))
{
// 读取下一个可用的 ID
string firstLine = reader.ReadLine();
if (!string.IsNullOrEmpty(firstLine))
{
nextId = int.Parse(firstLine);
}
// 读取每个待办事项
string line;
while ((line = reader.ReadLine()) != null)
{
string[] parts = line.Split('|');
if (parts.Length == 4)
{
var item = new TodoItem(int.Parse(parts[0]), parts[1])
{
IsCompleted = bool.Parse(parts[2]),
CreatedTime = DateTime.Parse(parts[3])
};
todoItems.Add(item);
}
}
}
Console.WriteLine($"✓ 已加载 {todoItems.Count} 条待办事项");
}
catch (Exception ex)
{
Console.WriteLine($"✗ 加载失败: {ex.Message}");
}
}
/// <summary>
/// 获取统计信息
/// </summary>
public void ShowStatistics()
{
int total = todoItems.Count;
int completed = todoItems.Count(t => t.IsCompleted);
int uncompleted = total - completed;
double completionRate = total > 0 ? (double)completed / total * 100 : 0;
Console.WriteLine("\n===== 统计信息 =====");
Console.WriteLine($"总数: {total}");
Console.WriteLine($"已完成: {completed}");
Console.WriteLine($"未完成: {uncompleted}");
Console.WriteLine($"完成率: {completionRate:F1}%");
Console.WriteLine("====================");
}
}
}
代码讲解:
List<TodoItem>:使用泛型集合存储所有待办事项LINQ:使用FirstOrDefault、Where、Count等方法查询数据StreamWriter/StreamReader:用于文件的读写操作- 数据格式:使用
|分隔符将对象的属性保存为一行文本
第四步:实现主程序
using System;
namespace TodoListApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 待办事项管理系统 =====");
Console.WriteLine("欢迎使用!\n");
TodoManager manager = new TodoManager();
bool running = true;
while (running)
{
ShowMenu();
string choice = Console.ReadLine();
Console.WriteLine();
switch (choice)
{
case "1":
AddTodo(manager);
break;
case "2":
manager.ShowAll();
break;
case "3":
MarkCompleted(manager);
break;
case "4":
DeleteTodo(manager);
break;
case "5":
manager.ShowStatistics();
break;
case "6":
manager.SaveToFile();
running = false;
break;
default:
Console.WriteLine("✗ 无效的选项,请重新选择!");
break;
}
if (running)
{
Console.WriteLine("\n按任意键继续...");
Console.ReadKey();
Console.Clear();
}
}
Console.WriteLine("\n感谢使用!再见!\n");
}
/// <summary>
/// 显示菜单
/// </summary>
static void ShowMenu()
{
Console.WriteLine("\n===== 待办事项管理 =====");
Console.WriteLine("1. 添加事项");
Console.WriteLine("2. 查看所有事项");
Console.WriteLine("3. 标记完成");
Console.WriteLine("4. 删除事项");
Console.WriteLine("5. 统计信息");
Console.WriteLine("6. 保存并退出");
Console.WriteLine("========================");
Console.Write("请选择操作: ");
}
/// <summary>
/// 添加待办事项
/// </summary>
static void AddTodo(TodoManager manager)
{
Console.Write("请输入待办事项: ");
string content = Console.ReadLine();
manager.AddTodo(content);
}
/// <summary>
/// 标记完成
/// </summary>
static void MarkCompleted(TodoManager manager)
{
Console.Write("请输入要标记的事项编号: ");
if (int.TryParse(Console.ReadLine(), out int id))
{
manager.MarkAsCompleted(id);
}
else
{
Console.WriteLine("✗ 请输入有效的编号!");
}
}
/// <summary>
/// 删除事项
/// </summary>
static void DeleteTodo(TodoManager manager)
{
Console.Write("请输入要删除的事项编号: ");
if (int.TryParse(Console.ReadLine(), out int id))
{
manager.DeleteTodo(id);
}
else
{
Console.WriteLine("✗ 请输入有效的编号!");
}
}
}
}
代码讲解:
- 使用
switch语句根据用户选择执行不同操作 Console.Clear():清屏使界面更整洁Console.ReadKey():等待用户按键,避免信息一闪而过- 将每个操作封装成独立方法,使主程序更清晰
🎮 运行演示
===== 待办事项管理系统 =====
欢迎使用!
✓ 已加载 0 条待办事项
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 1
请输入待办事项: 学习 C# 面向对象编程
✓ 添加成功!
按任意键继续...
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 1
请输入待办事项: 完成项目实战
✓ 添加成功!
按任意键继续...
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 2
===== 待办事项列表 =====
未完成:
[ ] 1. 学习 C# 面向对象编程
[ ] 2. 完成项目实战
===========================
按任意键继续...
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 3
请输入要标记的事项编号: 1
✓ 已标记为完成!
按任意键继续...
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 5
===== 统计信息 =====
总数: 2
已完成: 1
未完成: 1
完成率: 50.0%
====================
按任意键继续...
===== 待办事项管理 =====
1. 添加事项
2. 查看所有事项
3. 标记完成
4. 删除事项
5. 统计信息
6. 保存并退出
========================
请选择操作: 6
✓ 数据已保存!
感谢使用!再见!
📚 知识点总结
这个项目用到了以下知识点:
| 知识点 | 说明 | 在项目中的应用 |
|---|---|---|
| 面向对象 | 类、属性、方法 | TodoItem 和 TodoManager 类 |
| 集合 | List<T> | 存储所有待办事项 |
| LINQ | 查询和筛选 | FirstOrDefault、Where、Count |
| 文件 I/O | StreamWriter/StreamReader | 数据的保存和加载 |
| 字符串操作 | Split、IsNullOrWhiteSpace | 解析文件数据 |
| 异常处理 | try-catch | 处理文件读写异常 |
| 日期时间 | DateTime | 记录创建时间 |
| 方法重载 | override ToString() | 自定义对象显示方式 |
💡 代码优化建议
1. 使用 JSON 格式存储
使用 System.Text.Json 或 Newtonsoft.Json 可以更方便地序列化对象:
using System.Text.Json;
// 保存
string json = JsonSerializer.Serialize(todoItems);
File.WriteAllText(filePath, json);
// 加载
string json = File.ReadAllText(filePath);
todoItems = JsonSerializer.Deserialize<List<TodoItem>>(json);
2. 添加优先级功能
public enum Priority
{
Low,
Medium,
High
}
public class TodoItem
{
public Priority Priority { get; set; }
// ...
}
3. 添加截止日期
public class TodoItem
{
public DateTime? DueDate { get; set; }
public bool IsOverdue()
{
return DueDate.HasValue && DueDate.Value < DateTime.Now && !IsCompleted;
}
}
4. 实现自动保存
每次修改数据后自动保存:
public void AddTodo(string content)
{
// ... 添加逻辑
AutoSave();
}
private void AutoSave()
{
try
{
SaveToFile();
}
catch
{
// 静默失败,不打断用户操作
}
}
🚀 扩展功能建议
完成基本功能后,可以尝试添加:
搜索功能
- 按关键词搜索待办事项
- 按状态筛选(已完成/未完成)
- 按日期范围查询
分类管理
- 为待办事项添加分类标签
- 按分类查看和管理
提醒功能
- 设置提醒时间
- 显示即将到期的事项
数据导出
- 导出为 CSV 格式
- 导出为 Markdown 清单
撤销功能
- 实现操作历史记录
- 支持撤销上一步操作
批量操作
- 批量标记完成
- 批量删除已完成事项
🎯 练习任务
基础任务:完整实现上述代码,确保所有功能正常运行
进阶任务:添加编辑功能,允许修改待办事项的内容
挑战任务:实现优先级功能,支持高、中、低三个等级
超级挑战:实现分类功能,支持为每个待办事项设置分类标签
参考实现:添加编辑功能
// 在 TodoManager 类中添加
public void EditTodo(int id, string newContent)
{
var todo = todoItems.FirstOrDefault(t => t.Id == id);
if (todo == null)
{
Console.WriteLine("✗ 找不到该事项!");
return;
}
if (string.IsNullOrWhiteSpace(newContent))
{
Console.WriteLine("✗ 内容不能为空!");
return;
}
todo.Content = newContent;
Console.WriteLine("✓ 修改成功!");
}
// 在主程序的菜单中添加选项
case "7":
EditTodo(manager);
break;
static void EditTodo(TodoManager manager)
{
Console.Write("请输入要编辑的事项编号: ");
if (int.TryParse(Console.ReadLine(), out int id))
{
Console.Write("请输入新的内容: ");
string newContent = Console.ReadLine();
manager.EditTodo(id, newContent);
}
else
{
Console.WriteLine("✗ 请输入有效的编号!");
}
}
📝 项目小结
恭喜你完成了第二个项目!通过这个项目,你应该:
- ✅ 掌握了面向对象的基本应用
- ✅ 学会了使用
List<T>管理数据 - ✅ 理解了文件读写的基本操作
- ✅ 熟练使用 LINQ 进行数据查询
- ✅ 学会了如何设计类和方法
- ✅ 实现了数据的持久化存储
🎯 下一步
完成待办事项管理项目后,下一个项目我们要做一个"学生信息管理系统",学习更复杂的 CRUD 操作和数据统计功能!
💪 自我检测
- [ ] 能够独立定义类和属性
- [ ] 会使用
List<T>存储和管理数据 - [ ] 理解如何使用 LINQ 查询数据
- [ ] 掌握文件的读写操作
- [ ] 能够实现数据的持久化
- [ ] 知道如何处理用户输入和异常
第二个项目完成了!你已经掌握了面向对象和数据管理的基础!继续加油!💪
