C# 使接口实现异步

C# 使接口实现异步,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我目前正在尝试使用一些异步方法创建我的应用程序。 我所有的IO都是通过接口的显式实现完成的,我对如何使操作异步感到有点困惑 在我看来,在实施过程中我有两个选择: interface IIO { void DoOperation(); } 选项1: 异步执行隐式实现并等待隐式实现中的结果 class IOImplementation : IIO { async void DoOperation() { await Task.Factory.StartN

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

在我看来,在实施过程中我有两个选择:

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
}
class IOAsyncImplementation : IIO
{
    private Task DoOperationAsync()
    {
        return new Task(() =>
            {
                //DO ALL THE HEAVY LIFTING!!!
            });
    }

    #region IIOAsync Members

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

    #endregion
}
选项2: 异步执行显式实现并等待隐式实现的任务

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
}

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

这两个选项都不正确。您正在尝试异步实现同步接口。不要那样做。问题是当
DoOperation()
返回时,操作还没有完成。更糟糕的是,如果在操作期间发生异常(这在IO操作中非常常见),用户将没有机会处理该异常

您需要做的是修改接口,使其异步:

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

class IOImplementation : IIO
{
    public async Task DoOperationAsync()
    {
        // perform the operation here
    }
}
这样,用户将看到操作是
async
,他们将能够
等待它。这也迫使代码的用户切换到
async
,但这是不可避免的


另外,我假设在实现中使用
StartNew()
只是一个示例,您不需要它来实现异步IO。(而且
newtask()
更糟糕,甚至不起作用,因为您不
Start()
任务
更好的解决方案是为异步操作引入另一个接口。新接口必须从原始接口继承

例如:

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。

可以使用抽象类代替接口(在C#7.3中)

//Like接口
抽象类IIO
{
公共虚拟异步任务操作(字符串名称)
{
抛出新的NotImplementedException();//抛出异常
//return wait Task.Run(()=>{return”“;});//或空do
}
}
//实施
类IOO实现:IIO
{
公共重写异步任务操作(字符串名称)
{
返回等待任务。运行(()=>
{
如果(姓名==“蜘蛛侠”)
返回“ok”;
返回“取消”;
}); 
}
}

如果有明确的实现,这会是什么样子?另外,您在哪里等待此实现?@Animal显式实现看起来与往常一样(只需添加
async
):
async Task IIO.DoOperationAsync()
。你是说你在哪里等待返回的任务?无论您在哪里调用
DoOperationAsync()
。基本上,我认为我可以将我的问题归结为“我在哪里等待?”如果我没有在异步方法中等待,我会收到编译警告。理想情况下,您不需要在
任务.Run()中包装IO代码,IO代码本身应该是异步的,您可以直接等待它。例如,
line=wait streamReader.readlinesync()
。那么使代码异步就没有多大意义了。请参阅文章为什么不使用
GetAwaiter().GetResult()
而不是
Wait()
?这样,您就不需要解包一个
aggregateeexception
来获取内部异常。一个变体依赖于实现多个(可能是显式)接口的类:
class Impl:IIO,IIOAsync
。然而,IIO和IIOAsync本身是不同的契约,可以避免将“旧契约”拉入新代码中<代码>变量c=新Impl();IIOAsync asasasync=c;IIO asSync=c
。这样做有什么好处?为什么
IIO.DoOperation()
有一个虚拟实现而不是
抽象的
本身?在C#7.3中,我不能使用没有主体的“抽象异步”方法。这样做没有任何用处。如果没有
abstract
成员,就没有必要让类
抽象化。
DoOperation()
方法可以是
抽象的
,但是你得到的东西本质上只是一个接口。这又让人想起,为什么不使用一个接口呢?上面的代码毫无意义:(“我不能使用没有主体的“抽象异步”方法”--没有理由想使用
abstract async
。在方法声明中添加
async
的唯一作用是允许您在方法中使用
await
。如果您的方法不使用
await
,那么您就不需要
async
。这就是为什么接口方法首先起作用的原因;实际重要的是返回类型。您可以在不使用
async
关键字的情况下创建返回类型
Task