Skip to main content
 Web开发网 » 站长学院 » 浏览器插件

面试 | .NET基础知识快速通关(4)

2021年10月11日5690百度已收录

面试 | .NET基础知识快速通关(4)  .net面试经验 第1张

【.NET】| 总结/Edison Zhou

此系列文章为我在2015年发布于博客园的.NET基础拾遗系列,它十分适合初中级.NET开发工程师在面试前进行一个系统的复习,因此我将其搬到公众号分享与你。

本文为第四篇,我们会对.NET的异常处理进行基础复习,全文会以Q/A的形式展现,即以面试题的形式来描述。

1 如何针对不同类型的异常进行捕捉?相信阅读本文的园友都已经养成了try-catch的习惯,但对于异常的捕捉和处理可能并不在意。确实,直接捕捉所有异常的基类:Exception 使得程序方便易懂,但有时这样的捕捉对于业务处理没有任何帮助,对于特殊异常应该采用特殊处理能够更好地引导规划程序流程。

下面的代码演示了一个对于不同异常进行处理的示例:

public class Program{ public static void Main(string[] args) { Program p = new Program(); p.RiskWork(); Console.ReadKey(); } public void RiskWork() { try { // 一些可能会出现异常的代码 } catch (NullReferenceException ex) { HandleExpectedException(ex); } catch (ArgumentException ex) { HandleExpectedException(ex); } catch (FileNotFoundException ex) { HandlerError(ex); } catch (Exception ex) { HandleCrash(ex); } } // 这里处理预计可能会发生的,不属于错误范畴的异常 private void HandleExpectedException(Exception ex) { // 这里可以借助log4net写入日志 Console.WriteLine(ex.Message); } // 这里处理在系统出错时可能会发生的,比较严重的异常 private void HandlerError(Exception ex) { // 这里可以借助log4net写入日志 Console.WriteLine(ex.Message); // 严重的异常需要抛到上层处理 throw ex; } // 这里处理可能会导致系统崩溃时的异常 private void HandleCrash(Exception ex) { // 这里可以借助log4net写入日志 Console.WriteLine(ex.Message); // 关闭当前程序 System.Threading.Thread.CurrentThread.Abort(); }}(1)如代码所示,针对特定的异常进行不同的捕捉通常很有意义,真正的系统往往要针对不同异常进行复杂的处理。

异常的分别处理是一种好的编码习惯,这要求程序员在编写代码的时候充分估计到所有可能出现异常的情况,当然,无论考虑得如何周到,最后都需要对异常的基类Exception进行捕捉,这样才能保证所有的异常都不会被随意地抛出。

(2)除此之外,除了在必要的时候写try-catch,很多园友更推荐使用框架层面提供的异常捕捉方案,以.NET为例:

WinForm,可以这样写:AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(UnhandledExceptionFunction);ASP.NET WebForm,可以在Application_Error()方法里捕获异常ASP.NET MVC,可以写ExceptionFilterASP.NET WebAPI,可以写ExceptionHandler2 了解Conditional特性吗?大家都知道,通常在编译程序时可以选择Debug版本还是Release版本,编译器将会根据”调试“和”发布“两个不同的出发点去编译程序。在Debug版本中,所有Debug类的断言(Assert)语句都会得到保留,相反在Release版本中,则会被通通删除。这样的机制有助于我们编写出方便调试同时又不影响正式发布的程序代码。

But,单纯的诊断和断言可能并不能完全满足测试的需求,有时可能会需要大批的代码和方法去支持调试和测试,这个时候就需要用到Conditional特性。Conditional特性用于编写在某个特定版本中运行的方法,通常它编写一些在Debug版本中支持测试的方法。当版本不匹配时,编译器会把Conditional特性的方法内容置为空。

下面的一段代码演示了Conditional特性的使用:

//含有两个成员,生日和身份证//身份证的第6位到第14位必须是生日//身份证必须是18位public class People{ private DateTime _birthday; private String _id; public DateTime Birthday { set { _birthday = value; if (!Check()) throw new ArgumentException(); } get { Debug(); return _birthday; } } public String ID { set { _id = value; if (!Check()) throw new ArgumentException(); } get { Debug(); return _id; } } public People(String id, DateTime birthday) { _id = id; _birthday = birthday; Check(); Debug(); Console.WriteLine("People实例被构造了..."); } // 只希望在DEBUG版本中出现 [Conditional("DEBUG")] protected void Debug() { Console.WriteLine(_birthday.ToString("yyyy-MM-dd")); Console.WriteLine(_id); } //检查是否符合业务逻辑 //在所有版本中都需要 protected bool Check() { if (_id.Length != 18 || _id.Substring(6, 8) != _birthday.ToString("yyyyMMdd")) return false; return true; }}public class Program{ public static void Main(string[] args) { try { People p = new People("513001198811290215", new DateTime(1988, 11, 29)); p.ID = "513001198811290215"; } catch (ArgumentException ex) { Console.WriteLine(ex.GetType().ToString()); } Console.ReadKey(); }}下图则展示了上述代码在Debug版本和Release版本中的输出结果:

① Debug版本:

面试 | .NET基础知识快速通关(4)  .net面试经验 第2张

② Release版本:

面试 | .NET基础知识快速通关(4)  .net面试经验 第3张

Conditional机制很简单,在编译的时候编译器会查看编译状态和Conditional特性的参数,如果两者匹配,则正常编译。否则,编译器将简单地移除方法内的所有内容。

3 如何避免在类型转换时的异常?我们经常会面临一些类型转换的工作,其中有些是确定可以转换的(比如将一个子类类型转为父类类型),而有些则是尝试性的(比如将基类引用的对象转换成子类)。当执行常识性转换时,我们就应该做好捕捉异常的准备。

面试 | .NET基础知识快速通关(4)  .net面试经验 第4张

当一个不正确的类型转换发生时,会产生InvalidCastException异常,有时我们会用try-catch块做一些尝试性的类型转换,这样的代码没有任何错误,但是性能却相当糟糕,为什么呢?异常是一种耗费资源的机制,每当异常被抛出时,异常堆栈将会被建立,异常信息将被加载,而通常这些工作的成本相对较高,并且在尝试性类型转换时,这些信息都没有意义。

So,在.NET中提供了另外一种语法来进行尝试性的类型转换,那就是关键字 is 和 as 所做的工作。

(1)is 只负责检查类型的兼容性,并返回结果:true 和 false。→ 进行类型判断

public static void Main(string[] args){ object o = new object(); // 执行类型兼容性检查 if(o is ISample) { // 执行类型转换 ISample sample = (ISample)o; sample.SampleShow(); } Console.ReadKey();}(2)as 不仅负责检查兼容性还会进行类型转换,并返回结果,如果不兼容则返回 null 。→ 用于类型转换

public static void Main(string[] args){ object o = new object(); // 执行类型兼容性检查 ISample sample = o as ISample; if(sample != null) { sample.SampleShow(); } Console.ReadKey();}两者的共同之处都在于:不会抛出异常!

综上比较,as 较 is 在执行效率上会好一些,在实际开发中应该量才而用,在只进行类型判断的应用场景时,应该多使用 is 而不是 as。

总结本文总结复习了.NET的异常处理相关的重要知识点,下一篇会总结.NET中字符串处理相关的重要知识点,欢迎继续关注!

参考资料(全是经典)朱毅,《进入IT企业必读的200个.NET面试题》

张子阳,《.NET之美:.NET关键技术深入解析》

王涛,《你必须知道的.NET》

评论列表暂无评论
发表评论
微信