当前位置: 首页 > news >正文

C#面试题及详细答案120道(41-50)-- 异常处理

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

文章目录

  • 一、本文面试题目录
      • 41. C#中的异常处理机制是什么?try/catch/finally的作用
      • 42. throw和throw ex的区别
      • 43. 什么是自定义异常?如何创建和使用?
      • 44. 哪些异常不需要显式捕获(非检查异常)?
      • 45. using语句的作用是什么?与IDisposable接口的关系
      • 46. 如何处理多线程中的异常?
      • 47. 异常处理对性能有什么影响?
      • 48. 简述AggregateException的作用
      • 49. 什么情况下应该使用异常?什么情况下不应该?
      • 50. 如何在代码中实现资源的自动释放?
  • 二、120道C#面试题目录列表

一、本文面试题目录

41. C#中的异常处理机制是什么?try/catch/finally的作用

C#的异常处理机制通过try/catch/finally语句块捕获和处理程序运行时的错误,防止程序崩溃并提供错误恢复的机会。其核心思想是将可能引发错误的代码与处理错误的代码分离。

各部分作用

  • try块:包含可能引发异常的代码,是异常检测的范围。
  • catch块:捕获并处理try块中抛出的异常,可指定捕获特定类型的异常。
  • finally块:无论是否发生异常,都会执行的代码,通常用于释放资源。

使用示例

public class ExceptionHandlingExample
{public static void ReadFile(string path){FileStream stream = null;try{// 可能引发异常的代码stream = new FileStream(path, FileMode.Open);var reader = new StreamReader(stream);Console.WriteLine(reader.ReadToEnd());}// 捕获特定异常catch (FileNotFoundException ex){Console.WriteLine($"文件未找到: {ex.Message}");}// 捕获另一类异常catch (IOException ex){Console.WriteLine($"IO错误: {ex.Message}");}// 捕获所有其他异常(通常不推荐)catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}finally{// 确保资源释放,无论是否发生异常stream?.Dispose();Console.WriteLine("finally块执行完毕");}}
}

执行流程

  1. 正常执行:try块代码全部执行 → 跳过catch块 → 执行finally块。
  2. 发生异常:try块中异常点后的代码停止执行 → 匹配的catch块执行 → 执行finally块。
  3. 未捕获异常:try块停止执行 → 无匹配catch → 执行finally块 → 异常向上传播。

关键点

  • 一个try块可以搭配多个catch块(按异常类型从具体到抽象排序)。
  • finally块不是必需的,但通常用于释放资源(如文件句柄、数据库连接)。
  • 异常处理机制保证了程序在出错时仍能优雅地处理,而非直接崩溃。

42. throw和throw ex的区别

throwthrow ex都用于抛出异常,但它们在保留异常堆栈信息方面有本质区别,这对调试至关重要。

throw

  • 重新抛出当前捕获的异常,保留原始堆栈跟踪信息
  • 堆栈跟踪会包含异常最初发生的位置,以及重新抛出的位置。

throw ex

  • 重新抛出异常,但重置堆栈跟踪,将当前位置作为异常的起始点。
  • 丢失了原始异常发生的上下文信息,不利于调试。

示例代码

public class ThrowExample
{public static void Method1(){try{Method2();}catch (Exception ex){Console.WriteLine("Method1中捕获异常:");Console.WriteLine(ex.StackTrace); // 打印堆栈跟踪}}public static void Method2(){try{Method3();}catch (Exception ex){// throw ex; // 重置堆栈跟踪throw;      // 保留原始堆栈跟踪}}public static void Method3(){throw new InvalidOperationException("在Method3中发生错误");}
}

执行结果对比

  • 使用throw时,堆栈跟踪会显示异常起源于Method3,经过Method2重新抛出,最后在Method1捕获:

    在Method3中发生错误在 ThrowExample.Method3() 位置...在 ThrowExample.Method2() 位置...在 ThrowExample.Method1() 位置...
    
  • 使用throw ex时,堆栈跟踪会从Method2开始,丢失Method3的原始信息:

    在Method3中发生错误在 ThrowExample.Method2() 位置...  // 丢失了Method3的调用信息在 ThrowExample.Method1() 位置...
    

最佳实践

  • 当需要重新抛出异常(如在记录日志后),使用throw以保留完整堆栈信息。
  • 避免使用throw ex,除非明确需要截断堆栈跟踪(极少情况)。
  • 示例场景:在catch块中记录异常日志后,用throw将异常继续向上传播。

43. 什么是自定义异常?如何创建和使用?

自定义异常是根据业务需求创建的特定异常类型,继承自Exception类,用于区分不同类型的错误,使异常处理更精确。

创建自定义异常的规范

  1. 类名以Exception结尾(如InvalidOrderException)。
  2. 继承自Exception(直接或间接)。
  3. 实现三个构造函数:
    • 无参构造函数。
    • 带消息的构造函数。
    • 带消息和内部异常的构造函数(支持异常链)。
  4. 标记为[Serializable](支持序列化,用于跨应用域传递)。

示例:创建自定义异常

[Serializable]
public class InvalidOrderException : Exception
{// 无参构造函数public InvalidOrderException() : base("订单无效") { }// 带消息的构造函数public InvalidOrderException(string message) : base(message) { }// 带消息和内部异常的构造函数public InvalidOrderException(string message, Exception innerException) : base(message, innerException) { }// 序列化支持(必要时)protected InvalidOrderException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { }// 可添加自定义属性public int OrderId { get; set; }
}

使用自定义异常

public class OrderProcessor
{public void ProcessOrder(int orderId){try{if (orderId <= 0){// 抛出自定义异常throw new InvalidOrderException("订单ID必须为正数") { OrderId = orderId };}// 处理订单逻辑...if (orderId == 999){try{// 模拟内部错误throw new IOException("数据库连接失败");}catch (IOException ex){// 包装异常(异常链)throw new InvalidOrderException("处理订单时发生数据错误", ex){OrderId = orderId};}}}catch (InvalidOrderException){// 可以选择在此处理,或继续向上抛出throw; // 继续传播}}
}// 调用方处理
public static void Main()
{var processor = new OrderProcessor();try{processor.ProcessOrder(-1);}// 精确捕获自定义异常catch (InvalidOrderException ex){Console.WriteLine($"处理订单 {ex.OrderId} 失败: {ex.Message}");if (ex.InnerException != null){Console.WriteLine($"内部错误: {ex.InnerException.Message}");}}
}

优势

  • 使异常类型与业务逻辑紧密关联,提高代码可读性。
  • 允许调用方精确捕获特定异常,进行针对性处理。
  • 可携带额外信息(如OrderId),便于错误诊断。

44. 哪些异常不需要显式捕获(非检查异常)?

在C#中,异常分为非检查异常(Unchecked Exceptions)检查异常(Checked Exceptions),但C#仅支持非检查异常,即编译器不强制要求捕获或声明任何异常。不过,某些异常通常被视为"不应该显式捕获"的类型,因为它们代表了严重错误或编程错误。

通常不需要显式捕获的异常类型

  1. NullReferenceException:引用空对象时抛出,通常是编程错误(未初始化对象)。
  2. IndexOutOfRangeException:数组索引超出范围,属于编程错误。
  3. ArgumentNullException:方法参数为null但不允许,通常是调用方错误。
  4. ArgumentOutOfRangeException:参数值超出有效范围,属于调用方错误。
  5. InvalidCastException:类型转换失败,通常是编程错误。
  6. DivideByZeroException:除以零,属于逻辑错误。
  7. StackOverflowException:栈溢出,通常是递归过深等严重错误,无法有效处理。
  8. OutOfMemoryException:内存不足,严重错误,难以恢复。
  9. AccessViolationException:访问无效内存,通常是不安全代码导致的严重错误。

示例:不推荐捕获的情况

public void BadPractice()
{// 不推荐:捕获编程错误类异常try{string text = null;int length = text.Length; // 会抛出NullReferenceException}// 不推荐:掩盖了明显的编程错误catch (NullReferenceException){Console.WriteLine("发生了错误"); // 无法有效恢复}
}

原因

  • 这些异常通常由代码缺陷导致(如逻辑错误、参数校验缺失),而非运行时环境问题。
  • 捕获它们可能掩盖潜在的编程错误,导致调试困难。
  • 大多数情况下,这些异常无法在运行时有效恢复,应通过修正代码避免。

处理原则

  • 对于编程错误类异常(如NullReferenceException),应通过代码审查和测试消除,而非捕获。
  • 对于可能恢复的异常(如FileNotFoundException),应显式捕获并处理。
  • 可以在应用程序顶层(如全局异常处理)捕获所有未处理异常,记录日志并友好提示用户。

全局异常处理示例

// 控制台应用
public static void Main()
{try{// 应用程序入口RunApplication();}catch (Exception ex){// 记录所有未处理异常Log.Fatal("应用程序崩溃", ex);Console.WriteLine("发生未预期错误,请联系管理员");}
}// ASP.NET Core
public void Configure(IApplicationBuilder app)
{app.UseExceptionHandler(errorApp =>{errorApp.Run(async context =>{// 处理全局异常var exception = context.Features.Get<IExceptionHandlerFeature>().Error;Log.Error("未处理异常", exception);context.Response.StatusCode = 500;await context.Response.WriteAsync("服务器内部错误");});});
}

45. using语句的作用是什么?与IDisposable接口的关系

using语句用于确保实现了IDisposable接口的对象在使用后被正确释放资源,是一种简化资源管理的语法糖。

核心作用

  • 自动调用对象的Dispose()方法,释放非托管资源(如文件句柄、数据库连接、网络连接等)。
  • 即使发生异常,也能保证资源释放,替代了try/finally的繁琐写法。

与IDisposable接口的关系

  • using语句仅能用于实现IDisposable接口的类型。
  • IDisposable接口定义了Dispose()方法,用于释放资源。
  • using语句的编译结果本质是try/finally块,在finally中调用Dispose()

使用方式

  1. 声明式using(推荐)
// 语法:using (资源对象创建) { 使用资源 }
public void ReadFile(string path)
{// 创建实现IDisposable的对象using (var stream = new FileStream(path, FileMode.Open))using (var reader = new StreamReader(stream)){// 使用资源string content = reader.ReadToEnd();Console.WriteLine(content);} // 离开作用域时自动调用Dispose()
}
  1. 语句式using(C# 8.0+)
public void ReadFileModern(string path)
{// 无需大括号,作用域为当前方法using var stream = new FileStream(path, FileMode.Open);using var reader = new StreamReader(stream);string content = reader.ReadToEnd();Console.WriteLine(content);// 方法结束时自动调用Dispose()
}

编译后的等效代码

public void ReadFile(string path)
{FileStream stream = null;try{stream = new FileStream(path, FileMode.Open);StreamReader reader = null;try{reader = new StreamReader(stream);string content = reader.ReadToEnd();Console.WriteLine(content);}finally{reader?.Dispose(); // 释放reader}}finally{stream?.Dispose(); // 释放stream}
}

注意事项

  • using语句中的对象必须实现IDisposable接口,否则编译错误。
  • 多个using语句可以嵌套或并列(如示例1)。
  • 不要在using块外部使用已被Dispose()的对象(可能导致异常)。
  • 值类型(如struct)实现IDisposable时,using语句仍有效,但通常不推荐这样做。

适用场景

  • 文件操作(FileStreamStreamReader)。
  • 数据库连接(SqlConnectionDbContext)。
  • 网络资源(HttpClientSocket)。
  • 任何需要显式释放的非托管资源。

46. 如何处理多线程中的异常?

  • 原理说明:多线程环境中,线程抛出的异常若未捕获,可能导致程序崩溃。不同线程模型(如ThreadTask)的异常处理方式不同:
    • Thread类:异常需在线程内部捕获,外部无法直接捕获。
    • Task类(.NET 4.0+):异常会被包装为AggregateException,可通过Wait()ResultGetAwaiter().GetResult()捕获,也可在async/await中直接用try/catch
  • 示例代码
    // 1. Thread类处理异常(必须在内部捕获)
    var thread = new Thread(() => {try {throw new Exception("线程内部异常");}catch (Exception ex) {Console.WriteLine($"线程内捕获:{ex.Message}");}
    });
    thread.Start();// 2. Task类处理异常(外部捕获)
    try {var task = Task.Run(() => {throw new Exception("Task内部异常");});task.Wait(); // 触发AggregateException
    }
    catch (AggregateException ex) {// 解开包装的实际异常ex.Handle(e => {Console.WriteLine($"捕获到Task异常:{e.Message}");return true;});
    }// 3. async/await处理异常
    async Task TestAsync() {try {await Task.Run(() => {throw new Exception("Async异常");});}catch (Exception ex) {Console.WriteLine($"Async捕获:{ex.Message}");}
    }
    

47. 异常处理对性能有什么影响?

  • 原理说明:异常处理的性能损耗主要体现在异常抛出时,而非try/catch结构本身:
    • try块本身几乎不影响性能,编译器仅标记异常处理范围。
    • 异常抛出时,CLR需收集调用栈信息、查找匹配的catch块,此过程耗时(可能比正常流程慢1000倍以上)。
    • 频繁抛出异常会显著降低程序性能,尤其在循环或高频调用场景中。
  • 示例与建议
    // 性能差:频繁抛出异常
    for (int i = 0; i < 1000; i++) {try {if (i % 2 == 0) throw new Exception();}catch { /* 处理 */ }
    }// 性能好:用条件判断避免异常
    for (int i = 0; i < 1000; i++) {if (i % 2 == 0) {// 直接处理,不抛异常}
    }
    
    • 建议:异常仅用于意外错误,可预见的情况(如参数验证)用条件判断处理。

48. 简述AggregateException的作用

  • 原理说明AggregateException是.NET中专门用于包装多个异常的类型,常见于并行操作(如TaskParallel)中,当多个任务同时抛出异常时,所有异常会被汇总到AggregateException中。
  • 主要作用
    • 统一管理多任务中的多个异常,避免单个异常覆盖其他异常。
    • 通过InnerExceptions属性获取所有异常列表。
    • 提供Handle()方法批量处理内部异常。
  • 示例代码
    try {// 并行执行多个可能抛异常的任务var task1 = Task.Run(() => throw new Exception("任务1失败"));var task2 = Task.Run(() => throw new Exception("任务2失败"));Task.WaitAll(task1, task2);
    }
    catch (AggregateException ex) {// 遍历所有内部异常foreach (var innerEx in ex.InnerExceptions) {Console.WriteLine($"捕获异常:{innerEx.Message}");}// 用Handle()处理异常(返回true表示已处理)ex.Handle(innerEx => {Console.WriteLine($"处理异常:{innerEx.Message}");return true;});
    }
    

49. 什么情况下应该使用异常?什么情况下不应该?

  • 应该使用异常的情况
    • 意外错误:如文件不存在、网络中断、数据库连接失败等超出正常流程的错误。
    • 不可恢复的错误:如内存不足、权限不足等导致功能无法继续执行的情况。
    • 跨层错误传递:在多层架构中(如业务层到UI层),用异常传递错误信息更简洁。
  • 不应该使用异常的情况
    • 可预见的控制流:如输入验证(“用户名不能为空”应通过条件判断提示,而非抛异常)。
    • 性能敏感场景:高频操作(如循环)中抛异常会严重影响性能。
    • 正常业务逻辑分支:如“用户登录失败(密码错误)”属于预期结果,无需抛异常。
  • 示例对比
    // 不推荐:用异常处理可预见情况
    bool IsPositive(int num) {try {if (num <= 0) throw new ArgumentException();return true;}catch { return false; }
    }// 推荐:用条件判断
    bool IsPositive(int num) {return num > 0;
    }
    

50. 如何在代码中实现资源的自动释放?

  • 原理说明:资源(如文件句柄、数据库连接)需显式释放,.NET通过IDisposable接口定义释放逻辑,配合using语句可实现自动释放(编译时转为try/finally)。
  • 实现方式
    1. 类实现IDisposable接口,在Dispose()方法中释放非托管资源。
    2. using语句包裹资源对象,确保离开作用域时自动调用Dispose()
  • 示例代码
    // 1. 实现IDisposable的类
    public class ResourceHolder : IDisposable {private bool _disposed = false;private IntPtr _unmanagedResource; // 非托管资源(如文件句柄)public void Dispose() {Dispose(true);GC.SuppressFinalize(this); // 告诉GC无需调用析构函数}protected virtual void Dispose(bool disposing) {if (_disposed) return;if (disposing) {// 释放托管资源(如其他IDisposable对象)}// 释放非托管资源if (_unmanagedResource != IntPtr.Zero) {// 释放逻辑(如CloseHandle等)_unmanagedResource = IntPtr.Zero;}_disposed = true;}// 析构函数:仅用于释放非托管资源(防止Dispose未被调用)~ResourceHolder() {Dispose(false);}
    }// 2. 使用using自动释放
    public void UseResource() {using (var resource = new ResourceHolder()) {// 使用资源} // 离开作用域时自动调用resource.Dispose()
    }
    
    • 注意:using可用于任何实现IDisposable的对象(如FileStreamSqlConnection等内置类型)。

二、120道C#面试题目录列表

文章序号C#面试题120道
1C#面试题及详细答案120道(01-10)
2C#面试题及详细答案120道(11-20)
3C#面试题及详细答案120道(21-30)
4C#面试题及详细答案120道(31-40)
5C#面试题及详细答案120道(41-50)
6C#面试题及详细答案120道(51-60)
7C#面试题及详细答案120道(61-75)
8C#面试题及详细答案120道(76-85)
9C#面试题及详细答案120道(86-95)
10C#面试题及详细答案120道(96-105)
11C#面试题及详细答案120道(106-115)
12C#面试题及详细答案120道(116-120)
http://www.dtcms.com/a/549678.html

相关文章:

  • Shell实用实例1000例3
  • 定制型网站一般价格水利厅网站集约化建设
  • 基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
  • 5-27 WPS JS宏数组元素添加删除应用2
  • 网站字体样式wordpress iis伪静态
  • YouBallin:重塑去中心化创作者平台的声誉体系
  • 云手机存在的意义是什么
  • 360免费wifi安全吗网站建设html代码优化
  • 长安大学门户网站是谁给做的石墨网站开发
  • xtuoj Digit String
  • 【代码随想录算法训练营——Day53】图论——110.字符串接龙、105.有向图的完全可达性、106.岛屿的周长
  • 【代码审计】ECShop_V4.1.19 SQL注入漏洞 分析
  • 桐城市住房和城乡建设局网站广东网广东网站建设
  • 网站后台与前台家具网站开发设计任务书与执行方案
  • 网站的文案物流的网站模板
  • ESP32在arduino环境下的离线安装 -- 理论上多个版本都有效
  • Windows的mklink创建符号链使用方法
  • 小清新网站风格正规接单赚佣金的平台
  • 应用APP开发程序编辑中的数据加密和解密以及签名使用解释技巧
  • Spring Boot3零基础教程,Function 各种写法,笔记97
  • 好文与笔记分享 A Survey of Context Engineering for Large Language Models(上)
  • 贵阳营销型网站建设wordpress用思源黑体
  • ksycopg2实战:Python连接KingbaseES数据库的完整指南
  • mediwiki 做网站做网站有发展吗
  • 如何做网站服务器映射计算机专业学做网站吗
  • 拟人AI GoCap:用机器学习打造真实玩家体验
  • cocos里UV坐标显示取值变化异常问题
  • 【Python】包管理神器-uv
  • 设计模式——抽象工厂方法(abstract factory)
  • 追剧狂人网页入口 - 免费影视在线观看网站