Подтвердить что ты не робот

Почему С# позволяет сделать переопределение async?

В С#, когда вы переопределяете метод, разрешается переопределять async, когда оригинальный метод не был. Это похоже на плохую форму.

Проблема, которая заставляет меня задуматься, заключается в следующем: я был вовлечен, чтобы помочь с проблемой тестирования нагрузки. Около 500 одновременных пользователей процесс входа в систему будет разбит на цикл перенаправления. IIS регистрировал исключения с сообщением "Асинхронный модуль или обработчик завершен, пока асинхронная операция все еще ожидает". Некоторые поиски заставили меня подумать, что кто-то злоупотреблял async void, но мои быстрые поиски через источник ничего не нашли.

К сожалению, я искал async\s*void (поиск в регулярном выражении), когда мне нужно было искать нечто вроде async\s*[^T] (при условии, что Task не был полностью укомплектован.. вы поняли смысл).

То, что я позже обнаружил, было async override void onActionExecuting в базовом контроллере. Понятно, что это была проблема, и это было так. Фиксация этого (что делает его синхронным на данный момент) разрешило проблему.

Но это оставило меня с вопросом: почему вы можете пометить переопределение как async, когда вызывающий код никогда не мог его ждать?

4b9b3361

Ответ 1

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


Что касается виртуальных методов void, вы можете переопределить одно без ключевое слово async (очевидно) и запустить в нем не ожидаемую задачу. То, что происходит, когда вы переопределяете его с ключевым словом async и используете await в теле. Вызывающий не дождался созданной задачи (так как "оригинальная" подпись void). Оба случая аналогичны *:

public override void MyVirtualMethod()
{
    // Will create a non awaited Task (explicitly)
    Task.Factory.StartNew(()=> SomeTaskMethod());  
}

public override async void MyVirtualMethod()
{
    // Will create a non awaited Task (the caller cannot await void)
    await SomeTaskMethod();  
}

Статья Стивена Клири содержит некоторые примечания относительно этого:

  • Авансовые методы, возвращающие Void, имеют определенную цель: сделать возможным асинхронные обработчики событий.
  • Вы должны предпочесть async Task для async void.

* Реализация SomeTaskMethod, базовая структура, SynchronizationContext и другие факторы могут и будут приводить к различным результатам для каждого из вышеуказанных методов.

Ответ 2

Вы можете переопределить метод async, потому что async не является частью сигнатуры метода. На самом деле async позволяет использовать ключевое слово await в вашем методе, создав в нем конечный автомат.
Вы можете найти дополнительную информацию об асинхронном использовании здесь: http://blog.sublogic.com/2012/05/14/async-isnt-really-part-of-your-method-signature/

Ответ 3

async не входит в "контракт". Это деталь реализации, которая, на мой взгляд, к сожалению, появляется не в том месте.

Совершенно правомерно изменить метод (не async), возвращая Task в async один (или наоборот) без изменения прерывания и не требуя повторной компиляции вызывающих абонентов.

Дальнейшее указание на то, что он не является частью контракта, заключается в том, что вам не разрешено отмечать функции как async внутри интерфейсов и, как здесь, вполне возможно переопределить async с помощью async или наоборот.