1. C#.NET初级面试题精选31~40道解析作为.NET技术栈的敲门砖C#基础知识的掌握程度直接影响开发者的职业发展路径。本系列面试题精选自一线互联网企业技术面试题库覆盖了初级开发者必须夯实的核心概念。以下是第31至40题的深度解析每道题都附带典型错误示例和实战场景说明。提示面试官通常会根据候选人对基础概念的解释深度来判断真实水平单纯背诵概念很容易被追问至哑口无言。1.1 接口与抽象类的本质区别典型面试题请说明C#中接口(Interface)与抽象类(Abstract Class)的三点核心区别技术要点解析实现方式接口使用interface关键字定义仅包含方法签名C#8.0前、属性和事件声明抽象类使用abstract class定义可以包含字段、具体方法和抽象方法// 接口示例 public interface ILogger { void Log(string message); // 默认public且不能有实现 } // 抽象类示例 public abstract class LoggerBase { protected string _logPath; // 可包含字段 public abstract void Log(string message); // 抽象方法 public void SetLogPath(string path) { // 具体方法 _logPath path; } }多继承支持类可以实现多个接口但只能继承单个抽象类实际应用当需要多重行为契约时如既需要可序列化又需要可比较接口是唯一选择版本控制接口新增方法会破坏所有实现类需C#8.0的默认接口方法缓解抽象类可以安全添加新具体方法踩坑案例某电商系统将折扣计算规则设计为抽象类后期需要支持多重折扣策略时被迫重构为接口。正确做法应优先考虑接口除非确实需要共享基类实现。1.2 委托与事件机制剖析高频问题描述委托(Delegate)与事件(Event)的关系并举例说明实际应用场景。本质理解委托是类型安全的函数指针事件是基于委托的发布-订阅模型事件本质上是加了限制的委托仅允许类内部触发// 温度监控系统示例 public class TemperatureMonitor { // 定义委托 public delegate void ThresholdExceededHandler(float currentTemp); // 声明事件 public event ThresholdExceededHandler OnThresholdExceeded; private float _threshold 30.0f; public void CheckTemperature(float current) { if(current _threshold) { OnThresholdExceeded?.Invoke(current); // 事件触发 } } } // 使用方 var monitor new TemperatureMonitor(); monitor.OnThresholdExceeded temp Console.WriteLine($警报当前温度{temp}℃);设计误区直接暴露委托字段应始终使用event关键字忘记null检查可用?.操作符简化事件命名不规范建议用On开头表示事件性能优化点高频触发的事件应考虑使用EventHandlerTEventArgs标准模式避免大量临时委托对象分配。2. 字符串操作与集合处理2.1 字符串截取的最佳实践面试题C#中Substring()与Span 截取字符串有何性能差异底层原理对比方法内存分配适用场景线程安全Substring新分配需要保留子字符串是Span零分配临时处理/只读操作否MemoryPool池化分配高频操作的缓冲区视情况// 危险示例大文件读取时用Substring导致内存溢出 var hugeText File.ReadAllText(1GB文件.txt); var partial hugeText.Substring(0, 100); // 仍持有原字符串引用 // 正确做法使用ReadOnlySpanchar ReadOnlySpanchar safeSpan hugeText.AsSpan(0, 100);实战技巧处理JSON/XML片段时优先使用Span避免分配需要修改内容时使用StringBuilder而非多次Substring注意Span不能在async方法中使用栈上类型限制2.2 集合接口的选用策略典型问题IList 与IEnumerable 在哪些场景下应该优先选用前者核心差异分析graph TD IEnumerableT --|基础接口| ICollectionT ICollectionT --|扩展接口| IListT IListT --|具体实现| ListT关键选择指标随机访问需求需要索引访问时必须使用IList修改需求IEnumerable 不支持Add/Remove等操作延迟执行IQueryable 继承IEnumerable 可能涉及数据库延迟查询性能陷阱// 低效做法多次迭代IEnumerable var users GetUsers(); // 返回IEnumerableUser if(users.Any()) { // 第一次迭代 foreach(var u in users) { // 第二次迭代 //... } } // 优化方案转换为List var userList users.ToList(); // 单次迭代3. 多线程与异步编程3.1 Task.Run与async/await的误区常见笔试题以下代码有什么问题public string GetData() { return Task.Run(() HttpClient.GetStringAsync(url)).Result; }问题诊断死锁风险.Result同步阻塞导致上下文死锁多余封装Task.Run包装本身就是异步的方法资源浪费额外线程池调度开销正确模式public async Taskstring GetDataAsync() { return await HttpClient.GetStringAsync(url); // 直接await异步方法 }进阶知识点ConfigureAwait(false)在库代码中的使用ValueTask对高频调用的优化CancellationToken的正确传递方式3.2 线程安全集合实战场景题实现一个多线程安全的访问计数器要求支持高并发递增提供快照读取功能避免读写锁竞争解决方案对比// 方案1Interlocked原子操作适合简单计数器 private long _count; public void Increment() Interlocked.Increment(ref _count); public long GetCount() Interlocked.Read(ref _count); // 方案2ConcurrentDictionary复杂场景 private ConcurrentDictionarystring, int _counter new(); public void Add(string key) _counter.AddOrUpdate(key, 1, (_,v) v1);性能测试数据方法100万次操作耗时(ms)内存分配(MB)lock语句45015Interlocked1200.1ConcurrentDictionary380854. 数据库交互与异常处理4.1 ADO.NET连接管理经典面试题using语句在数据库连接中的作用是什么以下代码有何改进空间SqlConnection conn new SqlConnection(connStr); try { conn.Open(); // 执行操作 } finally { conn.Close(); }深度优化建议资源泄漏风险Close()在异常时可能未被调用应使用using确保Dispose()连接池优化正确写法应复用连接池连接超时配置默认30秒可能不适用生产环境工业级代码示例var settings new SqlConnectionStringBuilder(connStr) { Pooling true, MaxPoolSize 100, ConnectTimeout 15 }; using var conn new SqlConnection(settings.ConnectionString); using var cmd conn.CreateCommand(); cmd.CommandText SELECT ...; await conn.OpenAsync(); // 异步打开 using var reader await cmd.ExecuteReaderAsync();4.2 异常处理最佳实践陷阱识别题找出以下异常处理的错误之处try { ProcessData(); } catch (Exception ex) { Logger.Write(ex.Message); throw ex; }问题清单信息丢失仅记录Message没有StackTrace异常封装破坏throw ex会重置调用栈吞没异常没有区分可恢复异常与致命错误企业级处理模式try { await ProcessDataAsync(); } catch (BusinessException ex) when (ex.IsRecoverable) { await RetryPolicy.ExecuteAsync(...); } catch (DbException ex) { _logger.Error(ex, 数据库操作失败); throw new RepositoryException(数据访问错误, ex); // 保留原始异常 } finally { _metrics.RecordRequest(); }关键经验永远不要catch不带任何操作的Exception这会导致难以诊断的幽灵问题。至少记录完整的异常信息。