第2章:C#核心基础高频面试题 —— 语法、面向对象、异常处理、泛型与反射

本章聚焦 C# 核心基础高频考点,覆盖值类型/引用类型、面向对象三大特性、异常处理机制、泛型与反射。每个知识点拆解为面试问题、考察点、回答框架与示例回答,附带追问路径与避坑提醒。项目案例部分提供真实微服务场景下的泛型仓储 + 反射映射实践,帮助你从“会答”到“能讲”。

值类型 vs 引用类型 · 回答框架 值类型 vs 引用类型 存储位置 栈 vs 堆 赋值行为 复制值/复制引用 装箱 & 拆箱 性能影响 String 特殊性 引用类型不可变 struct vs class 自定义值/引用 面试官常从“装箱拆箱”和“String特殊性”深挖性能与内存

面试问题 1:请解释 C# 中值类型与引用类型的区别?

🎯 考察点

内存分配(栈 vs 堆)、赋值语义、装箱/拆箱性能、string 特殊性、struct vs class。

📋 回答框架

① 定义与存储位置 → ② 赋值行为区别 → ③ 装箱拆箱影响 → ④ 典型类型举例(int/struct vs class/string)。

✏️ 简短示例回答

值类型(int, struct)存储在栈上,赋值时复制值;引用类型(class, string)存储在堆上,赋值时复制引用。string 虽为引用类型但不可变,每次修改创建新对象。装箱将值类型转为 object,产生堆分配和性能损耗。

🔍 可能追问

• 为什么 string 是引用类型却表现得像值类型?
• 如何避免频繁装箱?使用泛型还是特定重载?
• struct 和 class 在性能上的取舍?

⚠️ 避坑提醒

不要混淆“引用类型”与“传递引用”;方法参数加 ref 会传递引用本身的引用。另外,大量装箱会引发 GC 压力,优先使用泛型集合(如 List<int>)替代 ArrayList。

面试问题 2:请简述 C# 面向对象的三大特性(封装、继承、多态)及其实现?

🎯 考察点

基本概念、访问修饰符、继承与构造链、virtual/override 与 new 关键字、抽象类 vs 接口。

📋 回答框架

① 封装:访问控制(public/private/protected) → ② 继承:单继承、基类构造调用 → ③ 多态:运行时多态(virtual/override)与编译时多态(重载) → ④ 抽象类与接口的对比。

✏️ 简短示例回答

封装通过 private 字段 + public 属性实现数据隐藏;继承用 class B : A 表示 B 继承 A;多态通过父类引用指向子类对象,调用虚方法时执行子类重写版本。抽象类可包含实现,接口仅定义契约(C# 8 后允许默认实现)。

🔍 可能追问

• 接口与抽象类的本质区别?(设计意图、成员默认访问、构造器等)
• 多态性能开销?虚方法表如何工作?
• 如何避免菱形继承问题?C# 不支持多继承,但可多实现接口。

⚠️ 避坑提醒

子类重写方法必须使用 override,若用 new 关键字则隐藏而非重写,易引发误解。另外,构造函数中避免调用虚方法,因为子类构造可能尚未完成。

面试问题 3:请解释 C# 泛型与反射的基本用途及典型场景?

🎯 考察点

泛型类型安全/性能优势、约束(where)、协变逆变;反射获取元数据、动态调用、Attribute 读取。

📋 回答框架

① 泛型:类型参数、避免装箱、约束(class/struct/new()) → ② 反射:typeof/GetType()、Invoke、GetProperties → ③ 性能对比与优化方向(表达式树/源代码生成器)。

✏️ 简短示例回答

泛型 List<T> 代替 ArrayList 消除装箱和类型转换。反射通过 typeof(MyClass).GetMethod("Run").Invoke(obj, null) 动态调用方法,常用于插件系统、ORM 映射。泛型约束 where T : IComparable 保证类型具备比较能力。

🔍 可能追问

• 泛型约束有哪些?(class, struct, new(), 基类/接口)
• 反射性能差如何优化?使用 Delegate.CreateDelegate 或 Expression Tree 缓存?
• 协变(out)与逆变(in)在什么场景下使用?

⚠️ 避坑提醒

不要过度使用反射在热点路径上;可以使用 Convert.ChangeType 或 DynamicMethod 提升性能。泛型约束中 new() 要求无参构造函数,若需要带参构造可结合工厂模式。

📁 项目案例:订单中心微服务(泛型仓储 + 反射 DTO 映射)

项目名称OrderHub – 统一订单处理平台
业务背景电商平台订单生命周期管理,支持多渠道订单接入、状态流转、库存扣减与财务结算。
规模指标日处理 10 万订单,峰值 500 TPS;微服务 120 个,代码库 200 万行;存储采用 SQL Server 分库分表 + Redis 缓存。
技术栈.NET 8, ASP.NET Core, EF Core 7, RabbitMQ, Docker/K8s, Serilog, OpenTelemetry
核心难点① 泛型仓储在高并发下的性能瓶颈(大量反射调用导致 CPU 飙升);② 反射实现 DTO 与实体自动映射时,对象复制开销大;③ 泛型约束与多态之间的灵活平衡。
面试高频追问• 你如何优化泛型仓储中的反射调用?
• 为什么要用泛型仓储而不直接用 EF Core 的 DbSet?
• 你们如何保证批量操作时的类型安全性?
可迁移回答话术“我们使用编译时代码生成(Source Generator)替代运行时反射,将泛型方法展开为具体类型的方法,减少 CPU 开销。同时结合 Expression Tree 缓存属性赋值委托,将对象映射性能提升 60% 以上。对于泛型约束,我们利用 where T : IEntity 接口统一实体行为,避免滥用 marker interface。”
💡 面试加分点: 提到“源代码生成器”表明你关注 .NET 性能前沿;提到“Expression Tree 缓存”证明你理解反射替代方案;结合业务数据(TPS、分库分表)显示真实工程经验。
⚠️ 面试常见坑: 只说“我们用反射实现了通用映射”而不提优化方案,会被认为缺乏性能意识;或者把泛型仓储吹成万能钥匙,忽略 EF Core 本身对 IQueryable 的延迟执行优势。