使接口实现异步

我目前正在尝试使用一些异步方法来制作我的应用程序。 我所有的 IO 都是通过接口的显式实现来完成的,我对如何使操作异步感到有点困惑。

据我所知,在实现过程中我有两种选择:

interface IIO
{
void DoOperation();
}

执行一个隐式异步实现,并等待隐式实现中的结果。

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
}

执行显式的异步实现并等待来自隐式实现的任务。

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


#region IIOAsync Members


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


#endregion
}

这些实现中是否有一个比另一个更好,或者还有其他我没有想到的方法?

160559 次浏览

这两种选择都不正确。您正在尝试异步实现同步接口。别这样。问题是当 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);
}
}

附言。 重新设计您的异步操作,使它们返回 Task 而不是 void,除非您确实必须返回 void。

可以使用抽象类代替接口(在 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";
});
}
}

我根据 Svick 的回答创建了一个示例应用程序,发现在没有 async关键字的情况下调用 IOImplementation.DoOperationAsync()不会导致编译器/Visual Studio 警告。这是基于 VisualStudio2019和。NET Core 3.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
}
}