1. 程式人生 > >C#中如果用await關鍵字來await一個為null的Task對象會拋出異常

C#中如果用await關鍵字來await一個為null的Task對象會拋出異常

有意 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對象會拋出異常