簡體   English   中英

為什么 C# 編譯器處理空異步任務方法與僅返回 Task.CompletedTask 不同?

[英]Why does the C# compiler handle empty async Task methods different to just return Task.CompletedTask?

你好,

編輯:該帖子被編輯為不使用空方法以更清楚地了解我想知道的內容。

C# 編譯不使用await返回Task.CompletedTask的情況下不編譯async Task方法是否有原因?

有時有必要實現一個返回Task但沒有使用await的方法。

我想知道生成的 IL 代碼在這兩種方法之間是否存在如此不同的原因:

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}
public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

我創建了兩個 Fiddles,可以在其中查看生成的 IL 代碼。
async Taskhttps://dotnetfiddle.net/UQuJUh
Task.CompletedTaskhttps://dotnetfiddle.net/eRr4i1

那么編譯器不優化代碼以以相同方式處理這兩種情況的原因是什么? 謝謝你。

在這兩段代碼中處理異常的方式存在顯着差異,因此它們是不等價的:

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}

Console.WriteLine引發異常的情況下,異常實際上是在內部捕獲並存儲在結果Task中,由Task.IsFaulted指示。 只有當您在任務上使用await時,才會真正“拋出”異常。

public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

然而,這種情況是完全同步的; 來自Console.WriteLine的潛在異常甚至在任何任務可以被返回或處理之前就會被引發。 我認為這實際上比第一個選項更不安全,因為這種行為可能是出乎意料的。 迭代器對yield break; return Array.Empty<object>(); .

現在您的問題最初詢問了一個實際上是空的方法。 好吧,可能是該方法實際上不是空的; 將來它可能包含調試模式下的斷點、注入代碼或可能為 arguments 自動生成的檢查。 即使是空的方法體,仍然會生成空的try塊:

  .try
  {
    IL_0000:  leave.s    IL_0019
  }  // end .try
  catch [mscorlib]System.Exception 
  {

現在我可以想象,對於真正空的方法,可以將生成async方法的異常添加到編譯器中,但這只會使代碼復雜化,而沒有(可以說)真正的收益。 可能會有一個空的async方法(例如給其他可能想要在未來實現它的程序員一個提示),但是你不需要那么關心性能來保證這樣的優化(無論如何你都有一個簡單的解決方案)。

(有趣的旁注:C# 11 可能會從標有!!的參數中引入ArgumentNullException的自動引發。此時,異常實際上是在async /iterator 方法的代碼之外,在調用之后立即引發的,因此可以說方法體,或者它的結果,在技術上仍然是空的。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM