#c# #async-await #interface
#c# #асинхронное ожидание #интерфейс
Вопрос:
У меня есть интерфейс
public interface IUserFactory
{
User GetUser(string id);
void DeleteUser(string id);
}
Текущий класс, который реализует, использует синхронные функции.
Теперь у меня будет реализация, которая использует HttpClient, вызовы которого являются асинхронными.
Сейчас я делаю так, чтобы новый класс UserFactoryWeb реализовал два набора функций:
Task<User> GetUserAsync(string id)
Task DeleteUserAsync(string id)
и
User GetUser(string id)
void DeleteUser(string id)
методы интерфейса. В методах интерфейса я использую трюк:
var t = Task.Run(async code...)
t.Wait();
для предотвращения взаимоблокировок.
Есть ли другие варианты?
Кроме того, как вы можете поддерживать асинхронные вызовы в интерфейсах? Кажется сумасшедшим, что асинхронность не является частью подписи интерфейса.
Комментарии:
1. Что вы ожидаете
async
от определения интерфейса? ВозвратTask<T>
(как и любой интерфейс, который имеет возможность асинхронных реализаций) означает «результат может быть или не быть доступен, дождитесь завершения этой задачи, чтобы получить его»… но что именноasync
следует добавить к этому?2. Как правило, вы не должны запускать асинхронный код в методах синхронизации. Task.Run — это способ сделать это, но это может привести к взаимоблокировкам. Если вы используете в asp.net ядро вы могли бы создать промежуточное программное обеспечение, которое загружает пользователя в контекст. Я предполагаю, что вы в любом случае должны кэшировать информацию о пользователе, поэтому такая синхронизация имеет смысл, но, вероятно, это действительно старый интерфейс.
3. Например, вы найдете это в большинстве дескрипторов аутентификации и аутентификации, где асинхронная часть находится в конвейере по запросам. Вся информация сохраняется в идентификаторе пользователя, и вы используете ее синхронно. Вы также можете использовать шаблоны асинхронного создания с внедрением зависимостей, все зависит от варианта использования. Не могли бы вы поделиться дополнительной информацией об интерфейсе.
4.
t.Wait();
блокирует ли вызов5. @FilipCordas — Я думал, что выполнение асинхронной задачи через Task.Run — это способ избежать взаимоблокировок. Проблема в том, что мне пришлось бы изменить интерфейс на: Task<User> getUser Задача deleteUser, а затем обновить все реализации, включая синхронные, для его поддержки.
Ответ №1:
Давайте объясним, почему интерфейсам не нужно async
ключевое слово
- Метод, возвращающий
Task
/ValueTask
, не обязательно должен быть асинхронным методом (метод сasync
ключевым словом). - Асинхронный метод — это, по сути, компилятор, помогающий
- Возврат
Task
/ValueTask
(если не недействителен), - Размещение исключений для возвращаемого
Task
/ValueTask
(если не недействительно) - Разрешение использования
await
ключевого слова
- Возврат
- Асинхронный метод выполняется синхронно до тех пор, пока не достигнет первого
await
выражения, после чего метод приостанавливается до завершения ожидаемой задачи. - Метод Async не нуждается в an
await
, и в этом случае это просто компилятор для возвращаемого значения и исключения, если оно не является недействительным (см. Пункт1.1
и1.2
соответственно) - Вы можете использовать
await
все, что поддерживаетINotifyCompletion
, а возвращаемый тип предоставляет 3 элемента- Завершено
- Завершено
- Получить результат
Task
иValueTask
могут ожидаться независимо от того, были ли они возвращены асинхронным методом (см. Пункт 5)- Интерфейсы — это просто контракты между разработчиком и вызывающей стороной и не дают никаких указаний на то, как реализуется контракт, они просто содержат определения для группы связанных функций
- Определение таких членов не требует гарантии того, был ли метод подключен к
async
ключевому слову an (или каким-либо другим странным вещам, которые вы делаете в этом методе), только то, что он возвращаетTask
/ValueTask
Короче async
говоря, ключевое слово — это деталь реализации, контракт — это просто определение, определению все равно, что вы делаете в своем методе. Однако, если вы это сделаете или хотите, чтобы ваши абоненты знали об этом, вы хорошо документируете.
Чтобы доказать это, async
ключевое слово не имеет фактического отношения к компиляции сигнатуры метода или к тому, как вызываемый объект вызывает его. Давайте посмотрим на скомпилированный код здесь. Вы заметите, что метод фактически теряет async
ключевое слово и просто возвращает задачу. Тем не менее, компилятор выполняет множество других функций для реализации языковой функции.
Такой метод, как
public static async Task Test1()
Фактически компилируется для
[AsyncStateMachine(typeof(<Test1>d__0))]
public static Task Test1()
Дополнительные ресурсы
Комментарии:
1. Что происходит, когда он возвращает void?
2. @bpeikes он по-прежнему не нуждается
async
в определении интерфейса, интерфейсы — это просто контракты, а не детали реализации
Ответ №2:
Вы можете обернуть свой возвращаемый тип в Task следующим образом:
public interface IUserFactory
{
Task<User> GetUser(string id);
Task DeleteUser(string id);
}
Комментарии:
1. Похоже, это именно то, что уже предлагал ОП… Насколько я понял вопрос, они обеспокоены отсутствием
async
самого определения интерфейса … (но именно так я прочитал вопрос)
Ответ №3:
Добавление async
к сигнатуре метода вообще не изменяет контракт метода. В async
IL вообще нет эквивалента. Реализация async
языковой функции полностью находится в компиляторе C #.
Это довольно просто продемонстрировать на простом примере и с помощью декомпилятора IL.
public class C {
public async Task Example1(Task t) => await t;
public Task Example2(Task t) => t;
}
// snip
public Task Example1(Task t)
// snip
public Task Example2(Task t)
// snip