2016-10-05 1 views
2

Предупреждение компилятора CS4014 (вызов метода асинхронизации без ожидающего результата) не выдается в качестве предупреждения во время сборки, когда вызываемый метод находится в ссылочной сборке.Предупреждение компилятора CS4014, не испущенное во время сборки

Когда вызываемый метод находится в том же сборке, предупреждение правильно испускается.

Предупреждение о компиляторе : сигнализируется в Visual Studio, когда оба проекта содержатся в одном решении.

Разница, похоже, вызвана тем, что компилятор имеет только скомпилированную ссылочную сборку и Visual Studio с исходным кодом для обеих сборок.

Вопрос в следующем: почему эти два разных поведения? И есть ли способ получить предупреждение CS4014 во время компиляции?

Чтобы повторить эту конфигурацию поведения двух библиотек классов, оба имеют один код файла:

TestClassLibrary1

public class Class1 
{ 
    public static async Task<string> DoSomething() 
    { 
     return await Task.FromResult("test"); 
    } 
} 

TestClassLibrary2 (ссылки TestClassLibrary1)

public class Class2 
{ 
    public void CallingDoSomething() 
    { 
     Class1.DoSomething(); 
    } 
} 

Составление этих проектов будет завершена без предупреждений. Открытие их в одном и том же решении в Visual Studio приведет к тому, что в списке ошибок будет отображаться 1 ошибка, а красная squiggly линия под Class1.DoSomething().

+0

Как вы собираете сборки? Можете ли вы показать заявления, которые используете? –

+0

Я собираю сборки, используя Visual Studio -> Build Solution. Оба проекта скомпилированы со всеми предупреждениями ('/ warn: 4'). Если это поможет, я могу вставить аргументы в csc.exe, но они довольно длинные. –

+0

Хорошо, я думал, что вы запустили сборку вручную. –

ответ

4

Модификатор async позволяет писать код, который возвращает Task более удобно (с await), но это не имеет представительства в IL *. В скомпилированной сборке метод просто выглядит как public static Task<string> DoSomething компилятору, и вызов тех, кто не ожидает их результата, не вызывает предупреждение (даже если методы живут в одной сборке). Замените Class1.DoSomething на что-то еще, что возвращает задачу и должно быть ожидаемо (скажем Task.Delay(2000)), и вы также увидите, что компилятор не предупреждает. Однако, когда все исходные коды доступны, компилятор (и компилятор я имею в виду Roslyn) может идентифицировать метод как async, потому что модификатор все еще является частью дерева синтаксиса.

Так почему же компилятор не всегда предупреждает, когда вы вызываете метод, возвращающий Task без использования результата, независимо от того, было ли это написано с использованием async? Хороший вопрос. Хотя существует множество законных сценариев, в которых вы не ждете ожидания Task (потому что вы хотите, например, передать его Task.WhenAll), все это связано с хранением Task где-то в другом месте, что не вызвало бы предупреждения. Вызов метода, который возвращает Task и полностью отбрасывая результат, почти наверняка является ошибкой (и когда он преднамерен, есть elegant ways), поэтому это предупреждение существует в первую очередь.

Я подозреваю, что реализация этого предупреждения может использовать настройку (или замену новым предупреждением), но это наверняка знают только люди, работающие над компилятором.


*: это на самом деле не так; async имеют AsyncStateMachineAttribute, применяемые к ним для более удобной отладки. Но по какой-либо причине компилятор не использует это для определения методов async в сборках. И не должно это, возможно: нет ничего особенного в async с точки зрения Task, метод возвращает. Но если они хотели точно сохранить указанную семантику CS4104 (предупредите, если результат метода async не используется), это было бы одним из способов сделать это.

+0

Возможно, распространенность «Task.Run()» для запуска задач «огонь-и-забыть» означает, что ложная положительная ставка будет слишком высокой, чтобы сделать предупреждение безусловным. –

+0

Отличный ответ, спасибо! Я работаю с библиотекой MassTransit, которая использует шаблон, в котором он хранит задачу внутри себя и возвращает ту же задачу. У вызывающего есть два варианта: 1. ожидание возвращенной задачи, чтобы убедиться, что операция завершена правильно или 2. позволить MassTransit ждать Task и обрабатывать любые исключения. Таким образом, может быть законное использование методов вызова без ожидания. Возникли проблемы с репо на Roslyn GitHub? –

+0

@BenjaminWegman: этот шаблон использования лучше обслуживать, предлагая два метода: тот, который возвращает задачу, а другой - нет. Но если вы застряли в этом интерфейсе, метод расширения .Forget() ', который отбрасывает задачу намеренно, как в связанном ответе, может оказаться полезным. Что касается поднятия вопроса о Roslyn, я вообще не участвую в проекте, поэтому я не знаю культуру ошибок. В этом смысле я не способен сделать какое-либо утверждение, что «помогает». –

Смежные вопросы