26

考虑:

using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        C c = new C();
        c.FooAsync(); // warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
        ((I)c).FooAsync(); // No warning
    }
}

class C : I
{
    public async Task FooAsync()
    {
    }
}

interface I
{
    Task FooAsync();
}

如果我直接在c对象上调用异步方法,我会收到编译器警告。这里可能存在错误,所以我很高兴收到警告。

但是,如果我对接口方法进行相同的调用,我不会收到任何警告。在这段代码中很容易漏掉一个错误。

我怎样才能确保我不会犯这个错误?有什么模式可以用来保护自己吗?

4

5 回答 5

7

Main 不是异步的,所以它不能使用await. 这似乎稍微混淆了编译器消息。如果您将调用放入实际的异步方法中;

static void Main(string[] args)
{
    Task.Run(async () =>
                       {
                           C c = new C();
                           c.FooAsync();
                           ((I) c).FooAsync();
                       });
}

...两者都会发出警告。

第 10 行:因为没有等待此调用,所以在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。
第 11 行:因为没有等待此调用,所以在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。

编辑:似乎所有Task 在异步方法中返回的方法都会发出警告,除非您等待或分配它们;请注意,我们正在使用甚至没有提到异步的接口;

interface I
{
    Task FooAsync();
}

static void Main(string[] args)
{
    I i = null;

    i.FooAsync();             // Does not warn
    // await i.FooAsync();    // Can't await in a non async method
    var t1 = i.FooAsync();    // Does not warn

    Task.Run(async () =>
    {
       i.FooAsync();          // Warns CS4014
       await i.FooAsync();    // Does not warn
       var t2 = i.FooAsync(); // Does not warn
    });
}
于 2013-07-25T19:41:22.210 回答
3

此警告的逻辑似乎是:

  • async方法中,每当Task调用 -returning 方法时都会发出警告,但结果会被忽略
  • 在普通(非async)方法中,每当调用Task-returningasync方法时都会发出警告,但结果会被忽略

例如,看看这个(无意义的)代码:

Task NonAsyncMethod()
{
    AsyncMethod(); // warnig
    NonAsyncMethod(); // no warning

    return null; // to make the code compile
}

async Task AsyncMethod()
{
    AsyncMethod(); // warning
    NonAsyncMethod(); // warning
}

这就是您没有收到接口警告的原因:接口方法没有(也不能)标记为async.

我认为原因是在旧的预async代码中,例如调用task.ContinueWith()并忽略其结果是很常见的。如果在这种情况下也报告了警告,那么相对大量的旧正确代码会突然变成警告。

当错误的可能性很大时,应输出警告。我认为报告的案例比没有报告的案例更有可能是错误。所以对我来说,这种行为是有道理的。

如果您想确保不会犯此错误,请注意从非代码调用Task-returning 方法。async

于 2013-07-25T22:04:43.947 回答
1

我敢说,不可能在编译级别发出这个警告。为了支持我的观点,看看这个例子:

interface I
{
    Task Foo();
}

class A : I
{
    public Task Foo()
    {
    }
}

class B : I
{
    public async Task Foo()
    {
    }
}

public class Program
{
    private static void Main(string[] args)
    {
        I i;

        if (Console.ReadLine() == "1")
        {
            i = new A();
        }
        else i = new B();

        i.Foo();
    }
}

你的第一个想法可能是:但这是一个荒谬的情况。但是一些设计模式(一个例子是工厂方法)使用机制以非常动态的方式实例化派生类。

那么VS如何知道该方法是否是异步的呢?

于 2013-07-25T19:59:40.570 回答
0

我想你可能在这里要求太多了。

interface I
{
    void Foo();
}

class C {} // does not implement I

class Program
{
    static void Main(string[] args)
    {
        C c = new C();
        ((I)c).Foo(); // Generates no compiler warning
    }
}

然而,强制转换发生在运行时,并且没有诸如async在运行时(或在 CIL 中)之类的东西。编译器转换async Task Foo()Task Foo()作为state-machine协同例程实现的。

于 2013-07-25T20:57:51.273 回答
0

Lindhart.Analyser.MissingAwaitWarningNuGet 包会为您检查这一点。在您的项目中安装 nuget 包,当方法返回未等待的任务时,您将收到编译器警告。

更多信息

于 2022-02-21T13:24:37.403 回答