編譯器警告 (層級 1) CS4014
因為未等候此呼叫,所以在呼叫完成之前會繼續執行目前方法。請考慮將 'await' 運算子套用至呼叫的結果。
目前的方法呼叫傳回 Task 或 Task ,而且不可以套用 等候 運算子對結果的非同步方法。 對非同步方法的呼叫開始非同步工作。 不過,因為 await 運算子不適用,程式會繼續執行,而不等候工作完成。 在大部分情況下,這並不是您所預期的值。 通常呼叫方法的其他方面取決於呼叫的結果,或至少呼叫的方法必須完成,才能從包含呼叫的方法之前傳回。
相同的重要問題就是發生在呼叫的非同步方法所引發的例外狀況。 在方法中引發傳回 Task 或 Task 的例外狀況會在傳回的工作中。 如果沒有等候工作也不明確檢查例外狀況,例外狀況會遺失。 如果等候工作,則會擲回例外狀況。
最佳作法是,您必須等候呼叫。
只有在您確定不想等候非同步呼叫完成時,您應該考慮隱藏警告,因此呼叫方法不會引發任何例外狀況。 在這種情況下,您可以藉由指定呼叫的工作結果給變數來隱藏警告。
下列範例顯示如何產生警告,如何隱藏它和如何等候呼叫。
async Task CallingMethodAsync()
{
resultsTextBox.Text += "\r\n Entering calling method.";
// Variable delay is used to slow down the called method so that you can
// distinguish between awaiting and not awaiting in the program's output.
// You can adjust the value to produce the output that this topic shows
// after the code.
var delay = 5000;
// Call #1.
// Call an async method. Because you don't await it, its completion
// isn't coordinated with the current method, CallingMethodAsync.
// The following line causes warning CS4014.
CalledMethodAsync(delay);
// Call #2.
// To suppress the warning without awaiting, you can assign the
// returned task to a variable. The assignment doesn't change how
// the program runs. However, recommended practice is always to
// await a call to an async method.
// Replace Call #1 with the following line.
//Task delayTask = CalledMethodAsync(delay);
// Call #3
// To contrast with an awaited call, replace the unawaited call
// (Call #1 or Call #2) with the following awaited call. Best
// practice is to await the call.
//await CalledMethodAsync(delay);
// If the call to CalledMethodAsync isn't awaited, CallingMethodAsync
// continues to run and, in this example, finishes its work and returns
// to its caller.
resultsTextBox.Text += "\r\n Returning from calling method.";
}
async Task CalledMethodAsync(int howLong)
{
resultsTextBox.Text +=
"\r\n Entering called method, starting and awaiting Task.Delay.";
// Slow the process down a little so that you can distinguish between
// awaiting and not awaiting in the program's output. Adjust the value
// for howLong if necessary.
await Task.Delay(howLong);
resultsTextBox.Text +=
"\r\n Task.Delay is finished--returning from called method.";
}
在此範例中,您還是可以選擇呼叫 #1 或呼叫 #2,在它的呼叫端 (CallingMethodAsync) 和呼叫端的呼叫端 (startButton_Click) 之後的 unawaited async 方法 (CalledMethodAsync) 結束為止。 當呼叫的方法完成時,會顯示下列輸出中的最後一行。 在這個完整範例中呼叫 CallingMethodAsync 的事件處理常式的輸入與結束標記於輸出。
Entering the Click event handler.
Entering calling method.
Entering called method, starting and awaiting Task.Delay.
Returning from calling method.
Exiting the Click event handler.
Task.Delay is finished--returning from called method.
您可以使用 #pragma warning (C# 參考) 指示詞,來隱藏編譯器警告。
範例
下列 Windows Presentation Foundation (WPF) 應用程式包含前述範例的方法。 下列步驟會設定應用程式。
建立 WPF 應用程式,並將其命名為 AsyncWarning。
在 Visual Studio 程式碼編輯器中,選擇 [MainWindow.xaml] 索引標籤。
如果未顯示索引標籤,請在 [方案總管] 中開啟 MainWindow.xaml 的捷徑功能表,然後選擇 [檢視程式碼]。
在 MainWindow.xaml 的 [XAML] 檢視中,將程式碼取代為下列程式碼。
<Window x:Class="AsyncWarning.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Button x:Name="startButton" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="startButton_Click" /> <TextBox x:Name="resultsTextBox" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/> </Grid> </Window>
包含按鈕和文字方塊的簡單視窗會出現在 MainWindow.xaml 的 [設計] 檢視中。
如需 XAML 設計工具的詳細資訊,請參閱使用 XAML 設計工具建立 UI。 如需建置簡單 UI 的詳細資訊,請參閱 逐步解說:使用 Async 和 Await 存取 Web (C# 和 Visual Basic)的 < 建立 WPF 應用程式 > 和 < 設計簡單的 WPF MainWindow > 一節。
將 MainWindow.xaml.cs 中的程式碼取代成下列範例中的程式碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace AsyncWarning { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { resultsTextBox.Text += "\r\nEntering the Click event handler."; await CallingMethodAsync(); resultsTextBox.Text += "\r\nExiting the Click event handler."; } async Task CallingMethodAsync() { resultsTextBox.Text += "\r\n Entering calling method."; // Variable delay is used to slow down the called method so that you can // distinguish between awaiting and not awaiting in the program's output. // You can adjust the value to produce the output that this topic shows // after the code. var delay = 5000; // Call #1. // Call an async method. Because you don't await it, its completion // isn't coordinated with the current method, CallingMethodAsync. // The following line causes warning CS4014. CalledMethodAsync(delay); // Call #2. // To suppress the warning without awaiting, you can assign the // returned task to a variable. The assignment doesn't change how // the program runs. However, recommended practice is always to // await a call to an async method. // Replace Call #1 with the following line. //Task delayTask = CalledMethodAsync(delay); // Call #3 // To contrast with an awaited call, replace the unawaited call // (Call #1 or Call #2) with the following awaited call. Best // practice is to await the call. //await CalledMethodAsync(delay); // If the call to CalledMethodAsync isn't awaited, CallingMethodAsync // continues to run and, in this example, finishes its work and returns // to its caller. resultsTextBox.Text += "\r\n Returning from calling method."; } async Task CalledMethodAsync(int howLong) { resultsTextBox.Text += "\r\n Entering called method, starting and awaiting Task.Delay."; // Slow the process down a little so that you can distinguish between // awaiting and not awaiting in the program's output. Adjust the value // for howLong if necessary. await Task.Delay(howLong); resultsTextBox.Text += "\r\n Task.Delay is finished--returning from called method."; } } // Output with Call #1 or Call #2. (Wait for the last line to appear.) // Entering the Click event handler. // Entering calling method. // Entering called method, starting and awaiting Task.Delay. // Returning from calling method. // Exiting the Click event handler. // Task.Delay is finished--returning from called method. // Output with Call #3, which awaits the call to CalledMethodAsync. // Entering the Click event handler. // Entering calling method. // Entering called method, starting and awaiting Task.Delay. // Task.Delay is finished--returning from called method. // Returning from calling method. // Exiting the Click event handler. }
選取 F5 鍵執行程式,然後選擇 [開始] 按鈕。
預期的輸出會出現在程式碼結尾。