web-dev-qa-db-ja.com

インターフェイス実装を非同期にする

現在、いくつかの非同期メソッドを使用してアプリケーションを作成しようとしています。すべてのIOは、インターフェイスの明示的な実装を通じて行われ、操作を非同期にする方法について少し混乱しています。

物事を見ると、実装には2つのオプションがあります。

interface IIO
{
    void DoOperation();
}

OPTION1:暗黙的な実装の非同期を行い、暗黙的な実装で結果を待ちます。

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:明示的な実装の非同期を行い、暗黙的な実装からのタスクを待ちます。

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つは他の実装よりも優れていますか、または私が考えていない別の方法がありますか?

100
Moriya

これらのオプションはどちらも正しくありません。同期インターフェースを非同期に実装しようとしています。しないでください。問題は、DoOperation()が戻ったときに、操作がまだ完了していないことです。さらに悪いことに、操作中に例外が発生した場合(IO操作では非常に一般的です)、ユーザーはその例外に対処する機会がありません。

あなたがする必要があるのは、非同期になるようにインターフェースを変更するです。

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

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

このようにして、ユーザーは操作がasyncであることがわかり、awaitできるようになります。これにより、コードのユーザーはasyncに切り替えることもできますが、それは避けられません。

また、実装でStartNew()を使用することは単なる例であり、非同期IOを実装するためにそれを必要とするべきではないと思います。 (そしてnew Task()はさらに悪いです、あなたはStart()Taskをしないので、それはうまくいきません。)

200
svick

より良い解決策は、非同期操作用の別のインターフェイスを導入することです。新しいインターフェイスは、元のインターフェイスを継承する必要があります。

例:

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);
    }
}

追伸本当にvoidを返さなければならない場合を除き、非同期操作がvoidではなくTaskを返すように非同期操作を再設計します。

14
Dima