C#学习 Day2

今天学完你要达到这3个结果:

  • 会写:委托、Action/Func、事件、LINQ、泛型
  • 会讲:每个知识点的用途、优缺点、面试回答
  • 会用:把它们组合进一个小项目(订单管理)

模块1:委托 / Action / Func / Lambda(重点理解"函数也是数据")

1.1 委托是什么?

委托可以理解为:一个"方法类型"。

你可以把方法赋值给变量,再传来传去。

cs 复制代码
public delegate int Calc(int a, int b);

static int Add(int x, int y) => x + y;

static int Sub(int x, int y) => x - y;

Calc c = Add;

Console.WriteLine(c(3, 2)); // 5

c = Sub;

Console.WriteLine(c(3, 2)); // 1

1.2 为什么需要委托?

  • 解耦:调用方不关心具体实现
  • 扩展:同一流程可插不同算法
  • 常见于:回调、事件、LINQ、异步 API

1.3 Action / Func

  • Action<T>:有参数、无返回值
  • Func<T1,T2,...,TReturn>:有返回值
  • 实际项目里比自定义 delegate 用得更多
cs 复制代码
Action<string> logger = msg => Console.WriteLine($"[LOG]{msg}");

Func<int, int, int> add = (a, b) => a + b;

1.4 Lambda 表达式

Lambda 本质是匿名函数,常和委托搭配。
(x, y) => x + y 就是一个函数。


模块2:事件 event(面试和WPF都高频)

2.1 事件是什么?

事件是"受保护的委托",用于发布订阅模型。

发布者触发事件,订阅者被通知。

cs 复制代码
public class Stock
{
    public string Code { get; set; } = "";
    private decimal _price;

    public event Action<decimal, decimal>? PriceChanged; // oldPrice, newPrice

    public void UpdatePrice(decimal newPrice)
    {
        var old = _price;
        _price = newPrice;
        PriceChanged?.Invoke(old, newPrice);
    }
}

订阅:

cs 复制代码
var stock = new Stock { Code = "AAPL" };
stock.PriceChanged += (oldP, newP) =>
{
    var diff = newP - oldP;
    Console.WriteLine(diff >= 0 ? $"上涨 {diff}" : $"下跌 {diff}");
};

stock.UpdatePrice(100);
stock.UpdatePrice(108);

2.2 event 为什么必要?

如果只是 public Action ...,外部可以直接 Invoke,破坏封装。
event 限制外部只能 += / -=,触发权在类内部。

2.3 面试答法(一句话)

事件是基于委托的发布订阅机制,event 用于限制外部调用权限,保证封装与安全。


模块3:LINQ(今天最重要)

你面试至少要熟练这些:

  • Where:筛选
  • Select:投影
  • OrderBy/ThenBy:排序
  • GroupBy:分组
  • Any/All:存在性判断
  • FirstOrDefault:取首个或默认
  • Count/Sum/Max/Min:聚合统计

3.1 数据准备

cs 复制代码
public class Order
{
    public int Id { get; set; }
    public string CustomerName { get; set; } = "";
    public decimal Amount { get; set; }
    public DateTime CreatedAt { get; set; }
    public bool IsPaid { get; set; }
}

3.2 常见查询写法

cs 复制代码
var orders = new List<Order>
{
    new() { Id=1, CustomerName="Alice", Amount=1200, CreatedAt=DateTime.Today.AddDays(-1), IsPaid=true },
    new() { Id=2, CustomerName="Bob",   Amount=800,  CreatedAt=DateTime.Today,            IsPaid=false },
    new() { Id=3, CustomerName="Alice", Amount=500,  CreatedAt=DateTime.Today,            IsPaid=true },
    new() { Id=4, CustomerName="Cindy", Amount=2200, CreatedAt=DateTime.Today.AddDays(-2),IsPaid=true }
};

// 1) 金额>1000
var bigOrders = orders.Where(o => o.Amount > 1000).ToList();

// 2) 按客户分组统计总金额
var byCustomer = orders
    .GroupBy(o => o.CustomerName)
    .Select(g => new
    {
        Customer = g.Key,
        Total = g.Sum(x => x.Amount),
        Count = g.Count()
    })
    .OrderByDescending(x => x.Total)
    .ToList();

// 3) 最近一笔订单
var latest = orders.OrderByDescending(o => o.CreatedAt).FirstOrDefault();

// 4) 已支付数量
var paidCount = orders.Count(o => o.IsPaid);

// 5) 是否存在未支付大额订单
var hasRisk = orders.Any(o => !o.IsPaid && o.Amount > 1000);

3.3 LINQ 面试最常问:延迟执行

  • Where/Select 通常是延迟执行(不马上跑)
  • ToList()/Count()/First() 才会触发执行
  • 面试时你可以说:
    "延迟执行可减少不必要计算,也允许链式组合,但要注意数据源变化带来的结果变化。"

模块4:泛型(Generic)与约束

4.1 为什么不用 object?

object 需要强制转换,容易出错、也可能有装箱拆箱性能损耗。

泛型在编译期就类型安全。

4.2 泛型示例

cs 复制代码
public class Repository<T>
{
    private readonly List<T> _items = new();

    public void Add(T item) => _items.Add(item);
    public List<T> GetAll() => _items;
}

4.3 泛型约束(面试可答)

cs 复制代码
public class Service<T> where T : class, new()
{
    public T Create() => new T();
}

Day2 实战项目

项目名:OrderManager(控制台)

你要建的文件

  • Order.cs
  • IRepository.cs(可选)
  • Repository<T>.cs
  • OrderService.cs
  • Program.cs

功能清单(必须)

  1. 新增订单
  2. 查询全部订单
  3. 查询大额订单(阈值输入)
  4. 按客户分组统计总金额
  5. 标记订单已支付
  6. 显示统计:总金额、已支付金额、未支付数
  7. 订单创建后触发事件(打印日志)

结构建议

  • OrderService 负责业务
  • Repository<T> 负责存储
  • Program 只负责菜单交互

参考骨架(你可以直接照着写)

cs 复制代码
public class OrderService
{
    private readonly Repository<Order> _repo = new();

    public event Action<Order>? OrderCreated;

    public void CreateOrder(Order order)
    {
        _repo.Add(order);
        OrderCreated?.Invoke(order);
    }

    public List<Order> GetAll() => _repo.GetAll();

    public List<Order> GetBigOrders(decimal threshold)
        => _repo.GetAll().Where(o => o.Amount > threshold).ToList();

    public bool MarkPaid(int id)
    {
        var order = _repo.GetAll().FirstOrDefault(o => o.Id == id);
        if (order == null) return false;
        order.IsPaid = true;
        return true;
    }
}

今日面试题

1) 委托和事件区别?

  • 委托是方法类型;事件是对委托的封装,限制外部只能订阅/退订,不能触发。

2) 为什么优先用 Action/Func?

  • 内置、简洁、可读性好,减少重复定义 delegate。

3) Any 和 Count()>0 哪个好?

  • Any 更好,找到一个就返回;Count 常需遍历更多。

4) First 和 FirstOrDefault 区别?

  • First 找不到抛异常;FirstOrDefault 返回默认值(引用类型为 null)。

5) IEnumerable vs IQueryable?

  • IEnumerable 在内存中执行;
  • IQueryable 可转为表达式树,由数据库端执行(如 EF)。

6) 泛型的价值?

  • 复用 + 类型安全 + 减少装箱拆箱,提高性能和可维护性。
相关推荐
神谕的祝福1 小时前
comfyui从0到1开始学习-第三讲生图与降噪实验
学习
杰克尼1 小时前
天机学堂复习总结(day03-day04)
java·开发语言·redis·elasticsearch·spring cloud
星夜夏空992 小时前
STM32单片机学习(32) —— ADC
stm32·单片机·学习
x***r1512 小时前
jdk-11.0.16.1_windows使用步骤详解(附JDK 11环境变量配置与验证教程)
java·开发语言·windows
luck_bor3 小时前
File类&递归作业
java·开发语言
神仙别闹3 小时前
基于C#实现(WinForm)求解SIN(X)数值分析
c#
愚者Pro5 小时前
Flutter Widget组件学习(专为 Uniapp 转 Flutter 定制)
vue.js·学习·flutter·uni-app
努力努力再努力wz6 小时前
【Qt入门系列】:按钮组件全解析:从 QAbstractButton 到快捷键事件、单选与复选机制
c语言·开发语言·数据结构·c++·git·qt·github
yzx9910136 小时前
从焦虑到掌控:关于学习AI工具的深度思考
人工智能·学习