Создание реализаций интерфейса async

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

Как я вижу, у меня есть два варианта реализации:

interface IIO
{
    void DoOperation();
}

ОПЦИЯ1: Сделайте неявное выполнение async и ожидайте результат в неявной реализации.

class IOImplementation : IIO
{

     async void DoOperation()
    {
        await Task.Factory.StartNew(() =>
            {
                //WRITING A FILE OR SOME SUCH THINGAMAGIG
            });
    }

    #region IIO Members

    void IIO.DoOperation()
    {
        DoOperation();
    }

    #endregion
}

OPTION2: Сделайте явную реализацию async и ожидайте задачу из неявной реализации.

class IOAsyncImplementation : IIO
{
    private Task DoOperationAsync()
    {
        return new Task(() =>
            {
                //DO ALL THE HEAVY LIFTING!!!
            });
    }

    #region IIOAsync Members

    async void IIO.DoOperation()
    {
        await DoOperationAsync();
    }

    #endregion
}

Является ли одна из этих реализаций лучше, чем другая, или есть другой способ, о котором я не думаю?

Ответ 1

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

Что вам нужно сделать, так это изменить интерфейс, чтобы он был асинхронным:

interface IIO
{
    Task DoOperationAsync(); // note: no async here
}

class IOImplementation : IIO
{
    public async Task DoOperationAsync()
    {
        // perform the operation here
    }
}

Таким образом, пользователь увидит, что операция async, и они смогут await его. Это также в значительной степени заставляет пользователей вашего кода переключаться на async, но это неизбежно.

Кроме того, я предполагаю, что использование StartNew() в вашей реализации - всего лишь пример, вам не нужно, чтобы реализовать асинхронный ввод-вывод. (И new Task() еще хуже, это даже не работает, потому что вы не Start() Task.)

Ответ 2

Лучшее решение - ввести другой интерфейс для асинхронных операций. Новый интерфейс должен наследовать от исходного интерфейса.

Пример:

interface IIO
{
    void DoOperation();
}

interface IIOAsync : IIO
{
    Task DoOperationAsync();
}


class ClsAsync : IIOAsync
{
    public void DoOperation()
    {
        DoOperationAsync().GetAwaiter().GetResult();
    }

    public async Task DoOperationAsync()
    {
        //just an async code demo
        await Task.Delay(1000);
    }
}


class Program
{
    static void Main(string[] args)
    {
        IIOAsync asAsync = new ClsAsync();
        IIO asSync = asAsync;

        Console.WriteLine(DateTime.Now.Second);

        asAsync.DoOperation();
        Console.WriteLine("After call to sync func using Async iface: {0}", 
            DateTime.Now.Second);

        asAsync.DoOperationAsync().GetAwaiter().GetResult();
        Console.WriteLine("After call to async func using Async iface: {0}", 
            DateTime.Now.Second);

        asSync.DoOperation();
        Console.WriteLine("After call to sync func using Sync iface: {0}", 
            DateTime.Now.Second);

        Console.ReadKey(true);
    }
}

P.S. Перепроектируйте свои асинхронные операции, чтобы они возвращали Task вместо void, если вы действительно не должны возвращать void.