149

我目前正在尝试使用一些异步方法来制作我的应用程序。我所有的 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
}

选项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
}

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

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 重新设计你的异步操作,让它们返回 Task 而不是 void,除非你真的必须返回 void。

于 2017-04-05T16:07:41.920 回答
1

我根据 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
    }
}
于 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 回答