Share via


編譯器警告 (層級 1) CS4014

因為未等候此呼叫,所以在呼叫完成之前會繼續執行目前方法。請考慮將 'await' 運算子套用至呼叫的結果。

目前的方法呼叫傳回 TaskTask<TResult> ,而且不可以用 等候 運算子對結果的非同步方法。 對非同步方法的呼叫以開始非同步工作。 不過,,因為 await 運算子不適用,程式會繼續執行,而不等候工作完成。 在大部分情況下,這並不是您所預期的值。 通常會呼叫的方法的其他方面取決於呼叫的結果,或至少,呼叫的方法必須完成,才能從包含呼叫的方法之前傳回。

相同的重要問題就是發生在呼叫的非同步方法所引發的例外狀況。 在方法中引發傳回 TaskTask<TResult> 的例外狀況會在傳回的工作中。 如果沒有等候工作也不會明確檢查例外狀況,例外狀況會遺失。 如果等候工作,則會擲回例外狀況。

最佳作法是,您必須等候呼叫。

您應該考慮隱藏警告,只有在您確定不想等候非同步呼叫完成,且呼叫方法不會引發任何例外狀況。 在這種情況下,您可以藉由指定呼叫的工作結果隱藏警告給變數。

下列範例顯示如何產生警告,如何隱藏它和如何等候呼叫。

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) 應用程式包含前一個範例的方法。 下列步驟會設定應用程式。

  1. 建立 WPF 應用程式,並將其命名為 AsyncWarning。

  2. 在 Visual Studio 程式碼編輯器中,選取 [MainWindow.xaml] 索引標籤。

    如果這個選項不可見,請開啟 MainWindow.xaml 的捷徑功能表上的 [方案總管]],然後選取 [檢視程式碼]。

  3. 使用下列程式碼取代 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 > 一節。

  4. 使用下列程式碼取代 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.
    }
    
  5. 選取 F5 鍵執行程式,然後選取 [開始] 按鈕。

    預期的輸出會出現在程式碼結尾。

請參閱

參考

await (C# 參考)

概念

使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)