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 天前
C语言学习笔记 - 50.流程控制4 - 流程控制为什么非常非常重要
c语言·开发语言·笔记·学习
sunfdf1 天前
知识学习场景下的智能应用实践大纲
学习
在放️1 天前
Python 爬虫 · 第三方代理接入与合规使用
开发语言·爬虫·python
KANGBboy1 天前
java知识五(继承)
java·开发语言
c++之路1 天前
Bazel C++ 构建系列文档(三):构建第一个 C++ 项目
开发语言·c++
AI人工智能+电脑小能手1 天前
【大白话说Java面试题 第117题】【并发篇】第17题:线程有几种状态,之间如何转换?
java·开发语言·面试
MartinYeung51 天前
[论文学习]重新思考大型语言模型忘却目标:梯度视角与超越
人工智能·学习·语言模型
十月的皮皮1 天前
C语言学习笔记20260615-有序升序序列合并
c语言·笔记·学习
JAVA面经实录9171 天前
前端系统化学习计划表(含完整知识思维导图)
前端·学习
聚名网1 天前
域名net,com,cn有区别吗?有哪些不同呢?
服务器·开发语言·php