Зависит ли использование суффикса «Async» в имени метода от того, используется ли модификатор async?

107

Каково соглашение для добавления суффиксов к именам методов с помощью "Async"?

Следует ли добавлять суффикс «Async» только к методу, объявленному с помощью asyncмодификатора?

public async Task<bool> ConnectAsync()

Или достаточно просто возврата метода Task<T>или Task?

public Task<bool> ConnectAsync()
касперхдж
источник
4
Что касается наименования, в документе TAP говорится: Асинхронные методы в TAP включают суффикс Async после имени операции; например GetAsync для операции получения. Если вы добавляете метод TAP в класс, который уже содержит это имя метода с суффиксом Async, используйте вместо него суффикс TaskAsync. Например, если в классе уже есть метод GetAsync, используйте имя GetTaskAsync.
Джеймс Мэннинг
4
хорошо, я думаю, меня смутил заголовок вопроса «Соглашение об именах для асинхронных методов»
Джеймс Мэннинг
1
Это плохо сформулированный вопрос. Люди ссорятся, ответы неоднозначные.
Люк Пуплетт 08
4
Потому что многие люди неправильно его поняли и спорят о том, что задают, задаваясь вопросом, состоит ли этот вопрос из двух частей и т. Д. Доказательством того, что это сбивает с толку, является то, что люди сбиты с толку.
Люк Пуплетт 08
2
@DavidRR Я до сих пор не понимаю, сколько замешательства вызвал этот вопрос. Если ваши правки вносят некоторый порядок в путаницу, так что они помогли вам и, возможно, могут помочь другим, то я приветствую ваши правки, потому что вы достигли того, чего я не мог в исходной формулировке. Вопрос сейчас настолько стар, что я с трудом могу вспомнить свое мышление, когда я его здесь задавал, поэтому первоначальное намерение менее важно. Ответ Люка отражает то, что не все были сбиты с толку. Я нашел это чрезвычайно полезным.
kasperhj

Ответы:

128

Я думаю, правда неоднозначна даже из документации Microsoft:

В Visual Studio 2012 и .NET Framework 4.5 любой метод, которому присвоено asyncключевое слово ( Asyncв Visual Basic), считается асинхронным методом, а компиляторы C # и Visual Basic выполняют необходимые преобразования для реализации метода асинхронно с помощью TAP. Асинхронный метод должен возвращать либо объект, Taskлибо Task<TResult>объект.

http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx

Это уже не так. Любой метод с asyncявляется асинхронным, а затем говорится, что он должен возвращать либо Taskили Task<T>- что не подходит для методов наверху стека вызовов, например, Button_Click или async void.

Конечно, вы должны учитывать, в чем смысл конвенции?

Можно сказать, что Asyncсуффиксное соглашение заключается в том, чтобы сообщить пользователю API, что метод ожидает. Чтобы метод был ожидаемым, он должен возвращать Taskзначение void или Task<T>метод, возвращающий значение, что означает, что только последний может иметь суффикс Async.

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

В этой цитате из документа Microsoft говорится:

По соглашению вы добавляете «Async» к именам методов, которые имеют модификатор Async или async.

http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_NamingConvention

При этом даже не упоминается, что ваши собственные возвращаемые асинхронные методы Taskнуждаются в Asyncсуффиксе, с чем, я думаю, мы все согласны.


Таким образом, ответ на этот вопрос может быть таким: оба. В обоих случаях вам нужно добавить Asyncк методам asyncключевое слово и вернуть Taskили Task<T>.


Я попрошу Стивена Туба прояснить ситуацию.

Обновить

Так я и сделал. А вот что написал наш добрый человек:

Если общедоступный метод является возвращающим Task и является асинхронным по своей природе (в отличие от метода, который, как известно, всегда выполняется синхронно до завершения, но по какой-то причине все еще возвращает Task), он должен иметь суффикс «Async». Это руководство. Основная цель здесь при именовании - сделать очевидным для потребителя функциональности, что вызываемый метод, скорее всего, не завершит всю свою работу синхронно; это, конечно, также помогает в случае, когда функциональность предоставляется как с синхронными, так и с асинхронными методами, так что вам нужно различать имена, чтобы различать их. То, как метод достигает своей асинхронной реализации, не имеет значения для именования: используется ли async / await для получения справки компилятора или типы и методы из System.Threading.Tasks используются напрямую (например, грамм. TaskCompletionSource) на самом деле не имеет значения, поскольку это не влияет на сигнатуру метода с точки зрения потребителя метода.

Конечно, из правил всегда есть исключения. Наиболее заметным в случае именования будут случаи, когда смысл существования всего типа заключается в предоставлении асинхронно-ориентированной функциональности, и в этом случае наличие Async для каждого метода было бы излишним, например, методы самой задачи, которые создают другие задачи .

Что касается асинхронных методов с возвратом пустоты, нежелательно иметь их в общедоступной области, так как вызывающий не имеет надежного способа узнать, когда асинхронная работа завершена. Однако, если вы должны публично раскрыть асинхронный метод, возвращающий пустоту, вы, вероятно, захотите иметь имя, которое передает, что асинхронная работа инициируется, и вы можете использовать здесь суффикс «Async», если это имеет смысл. Учитывая, насколько редким должен быть этот случай, я бы сказал, что это действительно индивидуальное решение.

Надеюсь, это поможет, Стив

Краткое руководство из вступительного предложения Стивена достаточно ясно. Он исключает, async voidпотому что необычно создавать общедоступный API с таким дизайном, поскольку правильный способ реализации асинхронной пустоты - это вернуть простой Taskэкземпляр и позволить компилятору использовать свою магию. Однако, если вы действительно хотите public async void, Asyncрекомендуется добавить . Прочие вершины стекаasync void методы такие как обработчики событий, обычно не являются общедоступными и не имеют значения / квалифицируются.

Что касается меня, это говорит мне, что если я задаюсь вопросом о суффиксе Asyncв async void, мне, вероятно, следует превратить его в, async Taskчтобы вызывающие абоненты могли его ждать, а затем добавить Async.

Люк Пуплетт
источник
20
Что ж, жаль, что у нас нет проверок времени компиляции для вызовов методов ... о, подождите. Если бы я назвать метод Get или GetAsync и не использовать Await от вызывающей стороны, компиляция будет не строить. Таким образом, это соглашение ГЛУПОЧКО и действительно противоречит многим руководящим принципам Microsoft Style, например, избегать таких вещей, как PersonStringили PriceDecimalтак, зачем использовать GetAsync- API-потребители асинхронных API-интерфейсов не должны беспокоиться об этом, поскольку запрос всегда возвращается после завершения всех задач в любом случае. Это глупо и действительно меня раздражает. Но это просто еще одно соглашение, о котором никто не знает, почему он там.
Петр Кула
2
@ppumkin: Как отметил Стивен, метод может легко быть асинхронным по своей природе без использования async / await, поэтому вызывающий не имеет никаких указаний, кроме имени, независимо от того, работает ли функция асинхронно.
Ганнобо
6
@ppumkin: отсутствие ожидания асинхронного метода по умолчанию приводит к предупреждению во время компиляции; не ошибка сборки.
Дастин Кливленд
7
Я считаю это соглашение глупым. Есть три автоматических указания на то, что метод является асинхронным: 1. Тип возвращаемого значения - Задача. 2. Завершение кода представляет собой ожидаемую подсказку. 3. IDE предупредит вас, подчеркнув зеленым цветом и представив предупреждение компилятора. Так что я полностью согласен с @ppumkin. Суффикс Async так же глуп, как если бы вы написали такое свойство: public Lazy <Customer> CustomerLazy. Кто бы это сделал! ??
Marco
2
@Marco Я поднял эту идею на GitHub, подумал, что это лучшее место, но я не заинтересовался этим, я думал, что получу: github.com/dotnet/core/issues/1464
Люк Пуплетт
68

Я создаю множество API-сервисов и других приложений, которые вызывают другие системы, в которых большая часть моего кода выполняется асинхронно.

Мое собственное практическое правило, которому я следую:

Если есть как неасинхронные, так и асинхронные методы, которые возвращают одно и то же, я добавляю к асинхронному методу Async. В противном случае нет.

Примеры:

Только один способ:

public async Task<User> GetUser() { [...] }

Тот же метод с двумя подписями:

public User GetUser() { [...] }

public async Task<User> GetUserAsync() { [...] }

Это имеет смысл, поскольку возвращаются одни и те же данные, но отличается только одно: только способ возврата данных , а не сами данные.

Я также думаю, что это соглашение об именах существует из-за необходимости ввести асинхронные методы и по-прежнему поддерживать обратную совместимость.

Я утверждаю, что в новом коде не следует использовать суффикс Async. Это так же очевидно, как возвращаемый тип String или Int, как упоминалось ранее в этом потоке.

Йонас Стенсвед
источник
8
Я согласен, особенно с тем, что обычно вам нужно идти «полностью асинхронно», и в этом случае суффикс избыточен - какой смысл добавлять его к 90% кода;)
Бартош
3
это лучшее решение. Не замечая этого, я делал то же самое в своих API.
Марко
2
Это намного лучше, чем суффикс «Async» для всех методов асинхронного приложения
Мариуш Ямро
Проблема с этой техникой заключается в том, что если вы создадите позже неасинхронную версию, вы не сможете использовать ваше предпочтительное имя «GetUser ()».
Дэвид
3
Это прагматичный путь. Добавление Async к каждому методу с модификатором async - это просто венгерская нотация 2019. @David, если вы в конечном итоге добавите неасинхронную версию позже, переименуйте методы и следуйте соглашению об именах или просто не делайте этого.
Скрымсли
25

Каково соглашение для добавления суффиксов к именам методов с помощью "Async".

В основе задач Asynchronous Pattern (TAP) диктует , что методы всегда должны возвращать Task<T>(или Task) и быть названы с асинхронным суффиксом; это отдельно от использования async. Оба Task<bool> Connect()и будут компилироваться и работать нормально, но вы не будете следовать соглашению об именах TAP.asyncTask<bool> Connect()

Должен ли метод содержать asyncмодификатор или достаточно, чтобы он просто возвращал Task?

Если тело метода (независимо от типа или имени возвращаемого значения) включает await, вы должны использовать async; и компилятор сообщит вам: «Оператор 'await' может использоваться только в асинхронном методе ...». Возврата Task<T>или Taskнедостаточно, чтобы избежать использования async. Подробнее см. Async (Справочник по C #) .

Т.е. какая из этих подписей верна:

Оба и должным образом следуют соглашениям TAP. Вы всегда можете использовать ключевое слово, но вы получите предупреждение компилятора «В этом асинхронном методе отсутствуют операторы 'await' и он будет работать синхронно ...», если тело не использует .asyncTask<bool> ConnectAsync()Task<bool> ConnectAsync()asyncawait

Ðаn
источник
1
Он имеет в виду, добавляете ли вы «Async» к имени метода, а не используете ли вы asyncключевое слово.
Servy
1
@ Обслуживает, использует ли ключевое слово или нет async- вторая часть вопроса.
Corak
2
@Servy Это вопрос из двух частей. Первая часть, как вы сказали, заключается в том, нужно ли добавлять «Async» к имени метода. Вторая часть - использовать asyncмодификатор или нет . См. Также примеры OP public async Task<bool> ConnectAsync()asyncмодификатором) vs public Task<bool> ConnectAsync()(без asyncмодификатора). Само имя метода в обоих случаях имеет суффикс «Async».
Corak
2
Это не вопрос из двух частей. Вопрос в том, следует ли добавлять «Async» к именам методов, которые возвращают, Taskили методов, которые имеют async Task.
kasperhj
3
@lejon: тебе следует улучшить вопрос; "против" Фрагменты кода проясняют, что вопрос (в целом) касается асинхронности, поскольку это единственное различие.
Ðаn
11

или достаточно, чтобы он просто возвращал Task?

Который. asyncКлючевое слово не является реальной проблемой здесь. Если вы реализуете асинхронность без использования asyncключевого слова, метод по-прежнему будет «асинхронным» в общем смысле.

Сервировка
источник
7

Так Taskи Task<T>оба awaitable типа, они представляют собой некоторые асинхронные операции. Или, по крайней мере, они должны представлять.

Вы должны добавить суффикс Asyncк методу, который в некоторых случаях (не обязательно во всех) не возвращает значение, а скорее возвращает оболочку для текущей операции. Обычно это оболочка Task, но в Windows RT она может быть IAsyncInfo. Следуйте своей интуиции и помните, что если пользователь вашего кода увидит Asyncфункцию, он или она будет знать, что вызов этого метода не связан с результатом этого метода и что он должен действовать соответственно.

Обратите внимание, что есть такие методы, как Task.Delayи, Task.WhenAllкоторые возвращаются, Taskно не имеют Asyncсуффикса.

Также обратите внимание, что есть async voidметоды, которые представляют собой асинхронный метод « выстрелил и забыл», и вам лучше знать, что метод построен таким образом.

Тони Петрина
источник
6

Я бы сказал, что он должен использовать суффикс Async, если он возвращает Task, независимо от того, объявлен метод с asyncмодификатором или нет.

Причина в том, что имя объявлено в интерфейсе. Интерфейс объявляет возвращаемый тип, которым является Task. Затем есть две реализации этого интерфейса: одна реализация реализует его с помощью asyncмодификатора, а другая - нет.

public interface IFoo
{
    Task FooAsync();
}

public class FooA : IFoo
{
    public Task FooAsync() { /* ... */ }
}

public class FooB : IFoo
{
    public async Task FooAsync() { /* ... */ }
}
Фред
источник
Это правда. Мы всегда и везде используем интерфейсы, и интерфейсы нельзя объявить асинхронными. Так что официальные руководства по использованию суффикса Async кажутся мне полной ерундой. Я думаю, что ключевое слово async - это просто деталь реализации, часть внутренней части метода и не должно влиять на его имя или что-то внешнее.
Аль Кепп
5

В разделе «Асинхронное программирование с использованием async и await (C #)» Microsoft предлагает следующие рекомендации:

Соглашение об именовании

По соглашению вы добавляете «Async» к именам методов, которые имеют модификатор async .

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

Я считаю это руководство неполным и неудовлетворительным. Означает ли это, что при отсутствии asyncмодификатора этот метод следует называть Connectвместо ConnectAsync?

public Task<bool> ConnectAsync()
{
    return ConnectAsyncInternal();
}

Я так не думаю. Как указано в кратком ответе по @Servy и более подробный ответ по @Luke Puplett , я считаю , что это уместно и в самом деле ожидать , что этот метод должен быть назван ConnectAsync(потому что она возвращает awaitable). В качестве дополнительной поддержки этого, @John Skeet в этом ответе на другой вопрос добавляется Asyncк имени метода независимо от наличия asyncмодификатора.

Наконец, еще один вопрос , рассмотреть этот комментарий по @Damien_The_Unbeliever :

async/awaitявляется внедрение детали ваших методов. Это важно не один йота объявлен ли ваш метод async Task Method()или просто Task Method(), насколько ваши абоненты заинтересованы. (Фактически, вы можете переключаться между этими двумя вариантами позже, но это не будет считаться критическим изменением.)

Из этого я делаю вывод, что именно асинхронный характер метода определяет, как ему следует называть. Пользователь метода даже не узнает, используется ли asyncмодификатор в его реализации (без исходного кода C # или CIL).

DavidRR
источник