2013-07-25 4 views
22

Рассмотрим:Нет предупреждения, когда я забыл `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(); 
} 

Если я вызываю метод асинхронной непосредственно на c объекта, я получаю предупреждение компилятора. Здесь есть потенциальная ошибка, поэтому я рад предупреждению.

Однако, если я делаю тот же вызов по методу интерфейса, я не получаю предупреждения. В этом коде было бы легко пропустить ошибку.

Как я могу гарантировать, что я не ошибаюсь? Есть ли образец, который я могу применить для защиты себя?

+1

Хороший вопрос ... Поскольку нельзя определить методы интерфейса async, я не ожидаю, что компилятор даст вам предупреждение в этом случае. Может быть, кто-то знает, делает ли R # (или есть плагин для него). –

+0

ReSharper не поймает его. – cadrell0

+0

Возможно, [NDepend] (http://www.ndepend.com/) может найти его с пользовательским запросом «CQL» во время процесса сборки, но поскольку «async» - это трюк компилятора и на самом деле не скомпилирован в ИЛ, я в этом сомневаюсь. –

ответ

6

Главное не асинхронно, поэтому он не может использовать await. Кажется, это немного путает сообщения компилятора. Если вы поместите вызовы в реальный метод async;

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(); 
    } 
} 

Ваша первая мысль может быть: Но это абсурдная ситуация. Но некоторые шаблоны проектирования (пример - фабричный метод), используя механизмы, которые производят производные классы очень динамичным способом.

Так как VS может узнать, является ли метод асинхронным или нет?

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 во время выполнения (или в КСС). Компилятор преобразует async Task Foo() в Task Foo(), реализованный как state-machine совместных процедур.

2

Логика для этого предупреждения, кажется:

  • в 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 -превращение методов из кода async.

+1

«будьте осторожны ...». Грумит, я ненавижу быть осторожным. –