C# 实战项目

学了 17 篇基础,是时候把它们串起来做点真东西了。这一章给三个完整、可运行的项目——学生成绩管理、简易 Web API、文件批量处理工具,分别覆盖 C# 三大主力场景:业务建模(OOP + LINQ + 序列化)、Web 后端(ASP.NET Core)、系统脚本(文件 IO + 异步)。每个项目都从需求出发,给出完整代码、运行结果和要点讲解。建议你亲手跑一遍,再改改需求,这才是真正"上手"。

实战 1:学生成绩管理(class + List<T> + LINQ + JSON)

需求:做一个学生成绩管理小程序——定义 Student 类,装入若干学生(每人多科成绩),算均分、按均分排名、筛优秀学生;能把数据序列化到 JSON 文件,下次启动能读回来。

涉及知识:类与属性、List<T> 集合、LINQ(OrderByDescending / Where / Select / 查询语法)、System.Text.Json 序列化、文件读写。无需第三方依赖,.NET 内置就够。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;

// 学生类:姓名 + 多科成绩,带一个算均分的方法
public class Student
{
    public string Name { get; set; } = "";
    public List<double> Scores { get; set; } = new();

    public double Average() => Scores.Count == 0 ? 0 : Scores.Average();

    public override string ToString() => $"{Name} 均分 {Average():F1}";
}

public class Program
{
    const string DataFile = "students.json";
    // WriteIndented = true 让 JSON 缩进好看;JsonNamingPolicy 不指定则用 PascalCase 原样
    static readonly JsonSerializerOptions Opt = new() { WriteIndented = true };

    static void Main()
    {
        // 1. 造一批数据(List<T> 集合初始化器 + target-typed new)
        var students = new List<Student>
        {
            new() { Name = "小明", Scores = new() { 90, 80, 85 } },
            new() { Name = "小红", Scores = new() { 95, 88, 92 } },
            new() { Name = "小刚", Scores = new() { 70, 100, 85 } },
        };

        // 2. 序列化到 JSON 文件
        string json = JsonSerializer.Serialize(students, Opt);
        File.WriteAllText(DataFile, json);
        Console.WriteLine($"已保存 {students.Count} 条到 {DataFile}");

        // 3. 从 JSON 文件反序列化回来(! 抑止 nullable 警告)
        var loaded = JsonSerializer.Deserialize<List<Student>>(File.ReadAllText(DataFile))!;

        // 4. LINQ 方法语法:按均分降序排名
        var rank = loaded.OrderByDescending(s => s.Average()).ToList();
        Console.WriteLine("成绩排名:");
        int i = 1;
        foreach (var s in rank)
            Console.WriteLine($"  {i++}. {s}");

        // 5. LINQ 查询语法:挑出均分超 85 的姓名,按名字排序
        var good = (from s in loaded
                    where s.Average() > 85
                    orderby s.Name
                    select s.Name).ToList();
        Console.WriteLine($"均分超 85: {string.Join(", ", good)}");
    }
}
// 输出:
// 已保存 3 条到 students.json
// 成绩排名:
//   1. 小红 均分 91.7
//   2. 小明 均分 85.0
//   3. 小刚 均分 85.0
// 均分超 85: 小红
//
// students.json 文件内容:
// [
//   { "Name": "小明", "Scores": [ 90, 80, 85 ] },
//   { "Name": "小红", "Scores": [ 95, 88, 92 ] },
//   { "Name": "小刚", "Scores": [ 70, 100, 85 ] }
// ]

要点讲解:

实战 2:简易 Web API(ASP.NET Core 最小 API)

需求:用 ASP.NET Core 写一个 Todo REST 服务,支持增删改查——GET /todos 列全部、GET /todos/5 取一条、POST /todos 新增、PUT 更新、DELETE 删除。数据存内存即可。

涉及知识:最小 API(MapGet/MapPost/...)、依赖注入(AddSingleton)、路由绑定、Results 状态码、curl 测试。先建项目:dotnet new web -o TodoApi。

// TodoApi/Program.cs — 用 ASP.NET Core 最小 API 写一个 Todo REST 服务
// 创建项目:dotnet new web -o TodoApi
// 运行:dotnet run
// 测试:curl http://localhost:5000/todos
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;

var builder = WebApplication.CreateBuilder(args);
// 把 List<Todo> 注册为单例,充当"内存数据库"
builder.Services.AddSingleton<List<Todo>>(_ => new()
{
    new() { Id = 1, Title = "学 C#", Done = false },
    new() { Id = 2, Title = "写 Web API", Done = false },
});

var app = builder.Build();

// GET /todos — 返回全部
app.MapGet("/todos", (List<Todo> db) => db);

// GET /todos/5 — 返回单条;找不到返回 404
app.MapGet("/todos/{id:int}", (int id, List<Todo> db) =>
    db.FirstOrDefault(t => t.Id == id) is { } todo
        ? Results.Ok(todo)
        : Results.NotFound(new { error = "未找到" }));

// POST /todos — 新增;参数从 body 自动绑定成 Todo 对象
app.MapPost("/todos", (Todo todo, List<Todo> db) =>
{
    todo.Id = db.Max(t => t.Id) + 1;
    db.Add(todo);
    return Results.Created($"/todos/{todo.Id}", todo);
});

// PUT /todos/5 — 更新
app.MapPut("/todos/{id:int}", (int id, Todo input, List<Todo> db) =>
{
    var todo = db.FirstOrDefault(t => t.Id == id);
    if (todo is null) return Results.NotFound();
    todo.Title = input.Title;
    todo.Done = input.Done;
    return Results.NoContent();
});

// DELETE /todos/5 — 删除
app.MapDelete("/todos/{id:int}", (int id, List<Todo> db) =>
{
    var removed = db.RemoveAll(t => t.Id == id);
    return removed > 0 ? Results.NoContent() : Results.NotFound();
});

app.Run();

// 模型类
class Todo
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public bool Done { get; set; }
}

// 测试(另开终端):
// $ curl http://localhost:5000/todos
// [{"id":1,"title":"学 C#","done":false},{"id":2,"title":"写 Web API","done":false}]
// $ curl -X POST http://localhost:5000/todos -H "Content-Type: application/json" \
//        -d '{"title":"跑起来","done":false}'
// $ curl http://localhost:5000/todos/3
// {"id":3,"title":"跑起来","done":false}

要点讲解:

实战 3:文件批量处理工具(Directory + LINQ + async/await)

需求:写一个命令行工具,扫描指定目录下所有文件,统计总大小、Top 3 最大文件、按扩展名分组汇总,最后把报告异步写入 report.txt。支持 dotnet run -- ./some/dir 传参。

涉及知识:Directory.EnumerateFiles 递归遍历、FileInfo、LINQ 分组聚合、async/await 异步文件写入、命令行参数 args。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

// 扫描指定目录所有文件,统计大小并按扩展名分组,异步写报告
class Program
{
    static async Task Main(string[] args)
    {
        var dir = args.Length > 0 ? args[0] : ".";
        if (!Directory.Exists(dir))
        {
            Console.WriteLine($"目录不存在: {dir}");
            return;
        }

        // 1. 递归枚举所有文件;EnumerateFiles 惰性枚举,大目录不爆内存
        var stats = new List<(string Path, long Size)>();
        foreach (var f in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories))
        {
            stats.Add((f, new FileInfo(f).Length));
        }

        // 2. LINQ 聚合:总大小、Top 3 最大、按扩展名分组
        long total = stats.Sum(s => s.Size);
        var top3 = stats.OrderByDescending(s => s.Size).Take(3).ToList();
        var byExt = stats
            .GroupBy(s => Path.GetExtension(s.Path).ToLowerInvariant())
            .Select(g => new { Ext = g.Key, Count = g.Count(), Size = g.Sum(x => x.Size) })
            .OrderByDescending(g => g.Size)
            .ToList();

        Console.WriteLine($"扫描 {stats.Count} 个文件,总大小 {total / 1024.0:F1} KB");
        Console.WriteLine("Top 3 最大文件:");
        foreach (var s in top3)
            Console.WriteLine($"  {Path.GetFileName(s.Path)} — {s.Size / 1024.0:F1} KB");

        // 3. 异步写报告;string.Join 用 
 换行拼接多行文本
        var lines = byExt.Select(g => $"{g.Ext}: {g.Count} 个, {g.Size / 1024.0:F1} KB");
        await File.WriteAllTextAsync("report.txt", string.Join("
", lines));
        Console.WriteLine("报告已写入 report.txt");
    }
}
// 运行:dotnet run -- ./src
// 输出:
// 扫描 42 个文件,总大小 158.3 KB
// Top 3 最大文件:
//   data.json — 28.5 KB
//   styles.css — 14.2 KB
//   app.js — 9.8 KB
// 报告已写入 report.txt

要点讲解:

小结

三个项目分别对应 C# 三大主力场景:业务建模(类 + LINQ + JSON)、Web 后端(ASP.NET Core 最小 API)、系统脚本(文件 + 异步)。它们都用到了前面学的基础——类、集合、LINQ、异常、文件 IO——只是组合起来解决真实问题。建议你至少把其中一个改造成自己的版本(给学生加"班级"字段、给 Todo API 加鉴权、给文件工具加"按修改时间过滤"),做过一遍比读十遍更扎实。

恭喜!到这里 C# 系列就真正完结了。接下来挑一个方向深入——Web 后端(ASP.NET Core + EF Core)、游戏开发(Unity)、跨平台桌面(.NET MAUI)、云原生(.NET Aspire)——边做边学,进步最快。

← 上一篇 C# 练习题

返回 C# 教程目录

✈️💬