百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-08-23 11:56 2 浏览


此系列文章为我在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,可以写ExceptionFilter
  • ASP.NET WebAPI,可以写ExceptionHandler

2 了解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版本:


② Release版本:


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

3 如何避免在类型转换时的异常?

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

当一个不正确的类型转换发生时,会产生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》

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...