[C#6] 8-异常增强

时间:2022-04-23
本文章向大家介绍[C#6] 8-异常增强,主要内容包括0. 目录、1. 在catch和finally块中使用await、2. 异常过滤器、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

0. 目录

C#6 新增特性目录

1. 在catch和finally块中使用await

在C#5中引入一对关键字await/async,用来支持新的异步编程模型,使的C#的异步编程模型进一步的简化(APM->EAP->TAP->await/async,关于C#中的异步编程模型的不是本篇文章的介绍重点,详细的资料请移步这里Asynchronous Programming Pattern)。在C#5中虽然引入了await/async,但是却有一些限制,比如不能再catch和finally语句块中使用,C#6中将不再受此限制。

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace csharp6
 6 {
 7     internal class Program
 8     {
 9         private static void Main(string[] args)
10         {
11             do
12             {
13                 Log(ConsoleColor.White, "caller method begin", true);
14                 CallerMethod();
15                 Log(ConsoleColor.White, "caller method end");
16             } while (Console.ReadKey().Key != ConsoleKey.Q);
17         }
18 
19         public static async void CallerMethod()
20         {
21             try
22             {
23                 Log(ConsoleColor.Yellow, "try ", true);
24                 throw new Exception();
25             }
26             catch (Exception)
27             {
28                 Log(ConsoleColor.Red, "catch await begin", true);
29                 await AsyncMethod();
30                 Log(ConsoleColor.Red, "catch await end");
31             }
32             finally
33             {
34                 Log(ConsoleColor.Blue, "finally await begin", true);
35                 await AsyncMethod();
36                 Log(ConsoleColor.Blue, "finally await end");
37             }
38         }
39 
40         private static Task AsyncMethod()
41         {
42             return Task.Factory.StartNew(() =>
43             {
44                 Log(ConsoleColor.Green, "async method begin");
45                 Thread.Sleep(1000);
46                 Log(ConsoleColor.Green, "async method end");
47             });
48         }
49 
50         private static void Log(ConsoleColor color, string message, bool newLine = false)
51         {
52             if (newLine)
53             {
54                 Console.WriteLine();
55             }
56             Console.ForegroundColor = color;
57             Console.WriteLine($"{message,-20} : {Thread.CurrentThread.ManagedThreadId}");
58         }
59     }
60 }

运行结果如下:

如果你细心的话会发现async method begin:6这一行的颜色居然不是我设置的绿色,而是白色,而且顺序也出现了错乱;而你再运行一次,它可能就是绿色了。这其实是由于我在Log方法(非线程安全的方法)里面的两行代码被多个线程争抢调用引起的:

1 Console.ForegroundColor = color;
2 Console.WriteLine($"{message,-20} : {Thread.CurrentThread.ManagedThreadId}");

我们可以做点小改动来让Log方法做到线程安全(在C#中有很多方式可以做到,这只是其中一种):

 1 [MethodImpl(MethodImplOptions.Synchronized)]
 2 private static void Log(ConsoleColor color, string message, bool newLine = false)
 3 {
 4     if (newLine)
 5     {
 6         Console.WriteLine();
 7     }
 8     Console.ForegroundColor = color;
 9     Console.WriteLine($"{message,-20} : {Thread.CurrentThread.ManagedThreadId}");
10 }

貌似有点跑题了,回归正题,在catch和finally语句块中支持await关键字并不需要IL指令的支持,也不需要CLR的支持,而仅仅是编译器做出的代码转换(await/async就像lambda一样到delegate一样)。具体的IL就不做展开了,太庞大了,贴个图看下大致的情况:

2. 异常过滤器

其实这个语言特性在VB,F#里面早就支持了,现在C#6里面也可以使用了。

1 try { … }
2 catch (Exception e) when (filter(e))
3 {
4     …
5 }

其中when这一块就是异常过滤器生效的地方,when后面跟一个表达式,表达式结果如果为true,则进入当前catch语句块。