現在、いくつかの非同期メソッドを使用してアプリケーションを作成しようとしています。すべての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つは他の実装よりも優れていますか、または私が考えていない別の方法がありますか?
これらのオプションはどちらも正しくありません。同期インターフェースを非同期に実装しようとしています。しないでください。問題は、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
をしないので、それはうまくいきません。)
より良い解決策は、非同期操作用の別のインターフェイスを導入することです。新しいインターフェイスは、元のインターフェイスを継承する必要があります。
例:
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を返すように非同期操作を再設計します。