149

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

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

interface IIO
{
    void DoOperation();
}

オプション1: 暗黙的な実装の非同期を実行し、暗黙的な実装で結果を待ちます。

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
}

オプション2: 明示的な実装を非同期にして、暗黙的な実装からのタスクを待ちます。

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

4

4 に答える 4

283

これらのオプションはどちらも正しくありません。非同期で同期インターフェースを実装しようとしています。そうしないでください。問題は、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

于 2013-01-22T13:00:02.297 に答える
29

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

例:

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

PS本当にvoidを返す必要がない限り、非同期操作を再設計して、voidではなくTaskを返すようにします。

于 2017-04-05T16:07:41.920 に答える
1

Svickの回答に基づいてサンプルアプリを作成しましたが、キーワードIOImplementation.DoOperationAsync()なしで呼び出しても、コンパイラー/VisualStudioの警告は表示されないことがわかりました。asyncこれは、VisualStudio2019および.NETCore3.1に基づいています。

以下のサンプルコード。

public interface ISomething
{
    Task DoSomethingAsync();
}
public class Something : ISomething
{
    public async Task DoSomethingAsync()
    {
        await Task.Run(() => Thread.Sleep(2000));
        Console.WriteLine("Message from DoSomethingAsync");

        throw new Exception("Some exception");
    }
}
class Program
{
    static void Main(string[] args)
    {
        ISomething something = new Something();
        
        Console.WriteLine("pre something.DoSomethingAsync() without await");
        something.DoSomethingAsync(); // No compiler warning for missing "await" and exception is "swallowed"
        Console.WriteLine("post something.DoSomethingAsync() without await");

        Thread.Sleep(3000);

        // Output:
        // pre something.DoSomethingAsync() without await
        // post something.DoSomethingAsync() without await
        // Message from DoSomethingAsync
    }
}
于 2021-07-09T21:01:52.627 に答える
-3

インターフェイスの代わりに抽象クラスを使用できます(C#7.3)。

// Like interface
abstract class IIO
{
    public virtual async Task<string> DoOperation(string Name)
    {
        throw new NotImplementedException(); // throwing exception
        // return await Task.Run(() => { return ""; }); // or empty do
    }
}

// Implementation
class IOImplementation : IIO
{
    public override async Task<string> DoOperation(string Name)
    {
        return await await Task.Run(() =>
        {
            if(Name == "Spiderman")
                return "ok";
            return "cancel";
        }); 
    }
}
于 2020-08-18T23:56:15.327 に答える