我目前正在尝试使用一些Async方法创建我的应用程序。 我的所有IO都是通过接口的显式实现来完成的,我对如何使操作异步感到困惑。
在我看到的情况下,我在实现中有两个选项:
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
}
这些实现中的一个是否比另一个好,还是有另一种方法可以解决这个问题?
答案 0 :(得分:193)
这些选项都不正确。您正在尝试异步实现同步接口。不要那样做。问题是当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
。)
答案 1 :(得分:11)
更好的解决方案是为异步操作引入另一个接口。新界面必须从原始界面继承。
示例:
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。
答案 2 :(得分:0)
我根据 Svick 的回答创建了一个示例应用,发现在没有 IOImplementation.DoOperationAsync()
关键字的情况下调用 async
不会导致编译器/Visual Studio 警告。这是基于 Visual Studio 2019 和 .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
}
}
答案 3 :(得分:-3)
可以使用抽象类代替接口(在C#7.3中)。
{{1}}