引言
C#开发者职位的面试不仅仅是语法检查。雇主希望看到对.NET平台的理解、CLR工作原理的知识、编写高性能代码的能力以及对现代方法(async、LINQ、DI)的掌握。
在本文中,我们将分析从A到Z的C#顶级问题——从基本数据类型到垃圾回收和异步编程等复杂主题。每个问题都附有简短答案和代码示例,让您在面试中充满信心。
1. 基础问题:类型、类和内存
1.1. 值类型和引用类型有什么区别?
值类型(int、struct、enum)存储在栈中,按值复制。引用类型(class、string、array)存储在堆中,变量包含对对象的引用。
int a = 10;int b = a; // b是副本,a和b相互独立
class MyClass { public int X; }MyClass obj1 = new MyClass { X = 5 };MyClass obj2 = obj1; // obj2引用同一个对象obj2.X = 10; // obj1.X也将变为101.2. 什么是装箱和拆箱?
装箱是将值类型包装成对象(放入堆中)。拆箱是相反的过程。频繁使用会降低性能。
int number = 42;object boxed = number; // 装箱int unboxed = (int)boxed; // 拆箱1.3. 请谈谈垃圾回收器(Garbage Collector)。
.NET中的GC自动释放没有引用的对象所占用的内存。它按代(第0代、第1代、第2代)工作。大对象(>85 KB)进入大对象堆(LOH)。
建议:不要手动调用GC.Collect()——这会破坏GC的优化机制。
2. 面向对象编程和设计原则
2.1. 解释SOLID原则。
- S — 单一职责:一个类应该只有一个引起变化的原因。
- O — 开闭原则:对扩展开放,对修改关闭。
- L — 里氏替换:子类必须能够替换其基类。
- I — 接口隔离:多个专用接口优于一个通用接口。
- D — 依赖反转:依赖抽象,而不是具体类。
2.2. 什么是依赖注入(DI)?
DI是一种设计模式,其中依赖项从外部传递给类(通过构造函数、方法或属性)。.NET Core内置了DI容器。
public interface ILogger{ void Log(string message);}
public class ConsoleLogger : ILogger{ public void Log(string message) => Console.WriteLine(message);}
public class OrderService{ private readonly ILogger _logger; public OrderService(ILogger logger) // 通过构造函数注入 { _logger = logger; }}3. LINQ、异常和字符串
3.1. LINQ是如何工作的?什么是延迟执行?
LINQ(语言集成查询)允许对集合、数据库(EF Core)和XML编写查询。延迟执行意味着查询仅在遍历结果时执行(例如,通过foreach或.ToList())。
var numbers = new List<int> { 1, 2, 3, 4, 5 };var query = numbers.Where(n => n > 2); // 查询未执行numbers.Add(6); // 在创建查询后添加foreach (var n in query) // 在此处执行 Console.WriteLine(n); // 3, 4, 5, 63.2. String和StringBuilder有什么区别?
String是不可变类型。每次连接操作都会创建一个新对象。StringBuilder使用可变缓冲区,对于多次字符串修改更高效。
// 不好(多次分配)string s = "";for (int i = 0; i < 1000; i++) s += i.ToString(); // 每次迭代都创建新字符串
// 好(使用可变缓冲区)StringBuilder sb = new StringBuilder();for (int i = 0; i < 1000; i++) sb.Append(i); // 使用同一个缓冲区4. 异步编程:async/await
4.1. async和await是如何工作的?
async关键字标记方法为异步。await暂停方法执行,直到等待的任务完成,同时不阻塞调用线程。编译器将方法转换为状态机。
public async Task<string> GetDataAsync(){ using var client = new HttpClient(); string result = await client.GetStringAsync("https://api.example.com"); return result;}4.2. 什么是死锁(deadlock)?如何避免?
当异步代码同步阻塞时(例如,使用.Result或.Wait())会发生死锁,尤其是在具有同步上下文的UI应用程序中。解决方案:使用ConfigureAwait(false)或完全避免同步阻塞。
// 危险:可能导致死锁string result = GetDataAsync().Result;
// 安全:使用awaitstring result = await GetDataAsync().ConfigureAwait(false);5. 高级问题:CLR、内存和性能
5.1. 什么是CLR?它如何工作?
CLR(公共语言运行时)是.NET的虚拟机。它负责内存管理、安全、异常处理和JIT编译(将IL代码转换为机器码)。
5.2. 什么是IDisposable?何时使用?
IDisposable用于释放非托管资源(文件、数据库连接、套接字)。实现Dispose方法,并使用using语句确保自动释放。
public class FileWriter : IDisposable{ private FileStream _stream; public void Dispose() { _stream?.Dispose(); }}
// 使用using (var writer = new FileWriter()){ // 使用writer} // 在此处自动调用Dispose结论
我们分析了C#面试中最常见的问题:从值类型和引用类型到异步编程和垃圾回收。关键不仅是知道答案,还要理解原理并能够用代码示例解释。
祝您面试顺利!记住:自信来自准备。练习编写代码,研究.NET内部机制,不要害怕承认您不知道的事情——诚实和学习的意愿总是受到重视。