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

Должны ли все интерфейсы быть перезаписаны для возврата Задача <Результат>?

У меня есть простой интерфейс

public interface SomethingProvider
{
    public Something GetSomething();
}

Чтобы "сделать" его асинхронным, я бы сделал это

public interface SomethingProvider
{
    public Task<Something> GetSomethingAsync();
}

Хотя интерфейс теперь намекает, что GetSomething является асинхронным, он позволяет синхронное выполнение, что прекрасно, если синхронный результат достаточно быстр. Если он блокируется, тогда я могу назначить вину плохой реализации интерфейса программистом-программистом.

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

В этом случае следует ли переписать все мои интерфейсы для возврата задач, если есть хоть малейшая вероятность, что вызов метода займет больше времени?

EDIT:
Я хотел бы подчеркнуть, что это не вопрос о том, какие задачи и как они работают, а вместо этого вопрос проектирования, связанный с присущей неизвестной реализации интерфейсов при их определении. При написании интерфейса полезно использовать синхронную и асинхронную реализацию.

Третьим "решением" может быть некоторое слияние двух предыдущих:

public interface SomethingProvider
{
    public Something GetSomething();
    public Task<Something> GetSomethingAsync();
}

но это нарушает принцип замещения Лискова и не добавляет ничего, кроме путаницы с разработчиком.

4b9b3361

Ответ 1

Эта проблема дизайна параллельна IDisposable. Когда вы пишете интерфейс, вы должны знать, будет ли он "вероятным", что производные типы будут нуждаться в IDisposable, и получить от него, если они будут. Небезопасные типы, такие как тестовые заглушки, просто реализуют noop Dispose.

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

Обратите внимание, что вопрос для производных типов: "Будет ли реализация естественной асинхронной?", не. Будет ли реализация быстрой? ". Скорость не имеет к этому никакого отношения. Единственное, что вам следует рассмотреть, это то, могут ли реализации быть естественно-асинхронными (то есть с использованием I/O или других асинхронных методов).

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

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

Как вы отметили, асинхронная подпись метода технически означает, что реализация может быть асинхронной. Тестовые заглушки и т.п. могут использовать Task.FromResult для синхронной реализации асинхронной подписи. Лично я считаю, что это вполне приемлемо для реализации заглушек; но я бы не рекомендовал использовать метод async "на всякий случай", когда все текущие реализации являются синхронными.

Кроме того, я категорически против наличия обеих подписей. Это потребовало бы, чтобы разработчики заверяли sync-over-async или асинхронную синхронизацию, ни одна из которых не является идеал.

Ответ 2

Это зависит от того, насколько "быстрым" вы ожидаете метод интерфейса. Например, если реализация проста, что-то вроде:

public Something GetSomething(){
    var s = new Something();
    if(...)s.SomeField = 1;
    return s;
}

Тогда возврат метода Task<> приведет к большему количеству накладных расходов, чем увеличение производительности. Весьма вероятно, что метод не имеет значительного влияния на производительность.

Но если он делает что-то вроде запроса DB, url или очень сложных вычислений, например:

public Something GetSomething(){
    Something s = Cache.Get("some key");
    if(s!=null)return s;
    var con = new SqlConnection(...);
    ...;
    return s;
}

Затем вы хотите пометить его Task<>.

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

Ответ 3

Когда вы создаете интерфейс с Task<>, вы говорите разработчикам вашего интерфейса: "Этот метод будет выполняться асинхронно, это вызовет работу, которая будет завершена когда-нибудь в будущем". Речь идет не о том, как "быстрый" метод будет запущен, метод async может быстро вернуться из веб-запроса. Верно, что await при операции async вызовет накладные расходы конечного автомата, но это определенно не гарантирует "скорость" выполнения.

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

Мне нравится определение Task from В чем разница между задачей и потоком? (с небольшой модификацией благодаря @akshay2000)

В терминах компьютерной науки задача - это будущее или обещание. (Некоторые люди используют эти два термина синонимно, некоторые используют их по-разному, никто не может договориться о точном определении.) В основном, задача "promises", чтобы вернуть вам T, но не сейчас, мед, не обращайтесь к вам, когда я готов