C#中如果用await關鍵字來await一個為null的Task對象會拋出異常
阿新 • • 發佈:2018-10-21
有意 ram bubuko exceptio class sys src mage http
await & async模式是C#中一個很重要的特性,可以用來提高異步程序(多線程程序)的執行效率。但是如果嘗試用await關鍵字來await一個為null的Task對象,會導致程序拋出NullReferenceException異常。
新建一個.NET Core控制臺項目,貼入如下代碼:
using System; using System.Threading; using System.Threading.Tasks; namespace AwaitNull { class Program { /// <summary>/// AwaitNullTask方法中的代碼會await一個為null的Task t,這樣做會拋出NullReferenceException異常 /// </summary> static async Task AwaitNullTask() { Task t = null;//聲明一個為null的Task對象t try { await t;//await為null的Task對象t,會導致這裏拋出NullReferenceException異常Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Await finished...");//由於上面拋出了異常,這裏的Console.WriteLine不會被執行 } catch(NullReferenceException e) { //輸出異常信息 Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : AwaitNullTask threw Exception : {e.GetType().ToString()}"); Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Exception message : {e.Message}"); throw;//catch後繼續拋出NullReferenceException異常到AwaitNullTask方法的外部 } } static void Main(string[] args) { Task taskReturned = AwaitNullTask();//很有意思的是雖然AwaitNullTask方法內部拋出了NullReferenceException異常,但是其並不會影響AwaitNullTask方法外部的方法,就好像AwaitNullTask方法是在另外一個線程上執行的一樣,但是本例中我們沒有用Task來啟動任何線程,可以看到本例中所有Console輸出的信息中Thread id都相同,是在同一個線程上 Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : taskReturned status is : {taskReturned.Status.ToString()}");//輸出AwaitNullTask方法返回的Task對象taskReturned的Status,由於AwaitNullTask方法內部拋出了異常,所以Task對象taskReturned的Status為Faulted Console.WriteLine(); Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Press any key to quit..."); Console.ReadKey(); } } }
輸出結果如下:
我們可以看到AwaitNullTask方法中由於await了一個為null的Task對象,拋出了NullReferenceException異常,但是有意思的是,AwaitNullTask方法表現出的行為是貌似運行在另一個線程上一樣,其拋出的NullReferenceException異常並不會影響到Main方法,但實際上Main方法和AwaitNullTask方法都是由同一個線程運行的。
所以我們在使用await關鍵字等待一個Task或Task<T>對象時,最好加上一個判斷邏輯,只有在Task或Task<T>對象不為null時,才進行await。所以我們更改本例中上面的代碼如下:
using System; using System.Threading; using System.Threading.Tasks; namespace AwaitNull { class Program { /// <summary> /// 現在我們在AwaitNullTask方法中加了判斷邏輯,只有在Task對象不為null時,才進行await,避免拋出異常 /// </summary> static async Task AwaitNullTask() { Task t = null;//聲明一個為null的Task對象t try { //判斷Task對象t是否為null,不為null才執行下面的await if (t != null) { await t;//因為現在上面的if存在,這裏的await就不會被執行了,避免了異常的拋出 Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Await finished..."); } } catch(NullReferenceException e) { //輸出異常信息 Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : AwaitNullTask threw Exception : {e.GetType().ToString()}"); Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Exception message : {e.Message}"); throw;//catch後繼續拋出NullReferenceException異常到AwaitNullTask方法的外部 } //返回類型為Task的異步方法(使用了async關鍵字的方法),可以不返回任何值,或者使用return;也可以,.NET會在異步方法結束時自動構造一個Task對象,作為異步方法的返回值 } static void Main(string[] args) { Task taskReturned = AwaitNullTask();//這次AwaitNullTask方法沒有拋出異常成功返回 Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : taskReturned status is : {taskReturned.Status.ToString()}");//由於AwaitNullTask方法成功返回,所以此時Task對象taskReturned的Status為RanToCompletion,表示AwaitNullTask方法成功執行完畢 //比較有意思的是雖然我們在AwaitNullTask方法中沒有return任何東西,但是AwaitNullTask方法還是返回了一個不為null的Task對象taskReturned,並且我們還在上面輸出了Task對象taskReturned的Status值,說明.NET為AwaitNullTask方法構造了一個Task對象作為返回值 Console.WriteLine(); Console.WriteLine($"Thread id <{Thread.CurrentThread.ManagedThreadId}> : Press any key to quit..."); Console.ReadKey(); } } }
輸出結果如下:
所以這次AwaitNullTask方法就不會拋出異常了,成功執行完畢。
C#中如果用await關鍵字來await一個為null的Task對象會拋出異常