Нет предупреждения, когда я забуду `await` при вызове метода интерфейса
Рассмотрим:
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();
}
Если я вызываю метод async непосредственно на объект c
, я получаю предупреждение о компиляторе. Там потенциально ошибка здесь, поэтому я рад за предупреждение.
Однако, если я делаю тот же вызов по методу интерфейса, я не получаю предупреждения. Было бы легко пропустить пропущенную ошибку в этом коде.
Как я могу гарантировать, что я не ошибаюсь? Есть ли образец, который я могу применить для защиты себя?
Ответы
Ответ 1
Main не является асинхронным, поэтому он не может использовать await
. Кажется, это немного путает сообщения компилятора. Если вы поместили вызовы в реальный метод асинхронного вызова,
static void Main(string[] args)
{
Task.Run(async () =>
{
C c = new C();
c.FooAsync();
((I) c).FooAsync();
});
}
... оба будут предупреждать.
Строка 10: поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Рассмотрите возможность применения оператора "ожидание" к результату вызова.
Строка 11: поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Рассмотрите применение оператора "ожидание" к результату вызова.
EDIT: Кажется, что все методы, возвращающие Task
внутри асинхронных методов, будут предупреждать, если вы не ждете или не назначаете их; обратите внимание, что мы работаем с интерфейсом, который даже не упоминает async;
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
});
}
Ответ 2
Я бы рискнул сказать, что невозможно сделать это предупреждение на уровне компиляции. чтобы поддержать мою точку зрения, посмотрите на этот пример:
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();
}
}
Ваша первая мысль может быть: Но это абсурдная ситуация. Но некоторые шаблоны проектирования (пример - метод factory), используя механизмы, которые производят производные классы очень динамичным способом.
Итак, как VS может узнать, является ли метод асинхронным или нет?
Ответ 3
Логика для этого предупреждения выглядит следующим образом:
- в методе
async
, предупреждать при вызове метода Task
-returning, но результат игнорируется
- в обычном (не
async
) методе, предупреждать при вызове метода Task
-returning async
, но результат игнорируется
Например, посмотрите на этот (бессмысленный) код:
Task NonAsyncMethod()
{
AsyncMethod(); // warnig
NonAsyncMethod(); // no warning
return null; // to make the code compile
}
async Task AsyncMethod()
{
AsyncMethod(); // warning
NonAsyncMethod(); // warning
}
Вот почему вы не получаете предупреждение с интерфейсом: метод интерфейса не (и не может) быть помечен как async
.
Я думаю, что причина в том, что в старом, pre- async
коде, он распространен, например, называть task.ContinueWith()
и игнорировать его результат. Если бы предупреждение также сообщалось в этом случае, относительно большое количество старого правильного кода внезапно стало бы предупреждением.
Предупреждение должно выводиться, если существует большая вероятность ошибки. Я думаю, что случаи, о которых сообщают, гораздо чаще становятся ошибками, чем случаи, которых нет. Поэтому для меня это имеет смысл.
Если вы хотите убедиться, что не допустили ошибку, будьте осторожны при вызове методов Task
-returning из кода <async
.
Ответ 4
Я думаю, вы можете слишком много просить здесь.
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
}
}
Тем не менее, кастинг происходит во время выполнения, и во время выполнения (или в CIL) нет ничего такого, как async
. Компилятор преобразует async Task Foo()
в Task Foo()
, реализованный как state-machine
из ко-подпрограмм.