使用Task <T>包装本机异步功能

Joe*_*ton 3 c# pinvoke native task-parallel-library async-await

我正在尝试围绕用本机代码编写的第三方库编写ac#wrapper,以便在我们的应用程序中使用,这些应用程序几乎都是用.NET编写的,我正在努力保持对C#模式的忠诚.这个库中的几乎所有调用本质上都是异步的,将所有异步调用包装到Task <T>对象中似乎是合适的.这是一个过于简单的示例,说明了本机库的结构:

delegate void MyCallback(string outputData);

class MyNativeLibrary
{
    public int RegisterCallback(MyCallback callback); // returns -1 on error
    public int RequestData(string inputData);         // returns -1 on error
}
Run Code Online (Sandbox Code Playgroud)

现在,我通过事件订阅提供了我的返回值,但我相信这将是一个更好的方法来返回我的数据:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData);
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,我一直未能找到适当的方法来实现这一点,而且我正在接触那些在使用Task <T>对象和async/await模式方面比我更有经验的人.

Dan*_*rth 9

你会用a TaskCompletionSource<TResult>来做这个.以下代码的内容:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData)
    {
        var completionSource = new TaskCompletionSource<string>();
        var result = Native.RegisterCallback(s => completionSource.SetResult(s));
        if(result == -1)
        {
            completionSource.SetException(new SomeException("Failed to set callback"));
            return completionSource.Task;
        }

        result = Native.RequestData(inputData);
        if(result == -1)
            completionSource.SetException(new SomeException("Failed to request data"));
        return completionSource.Task;
    }
}
Run Code Online (Sandbox Code Playgroud)

此答案假定不会同时调用此方法.如果有,你需要一些方法来区分不同的电话.许多API提供了一个userData有效负载,您可以将其设置为每次调用的唯一值,以便您可以区分.


Jon*_*eet 6

这听起来像你在寻找TaskCompletionSource<T>.您可以通过创建TaskCompletionSource,创建实例MyNativeLibrary和注册回调来包装您的库,该回调设置任务完成源的结果,然后从同一实例请求数据.如果其中任何一个步骤失败,请在任务完成源上设置错误.然后只需将TaskCompletionSource<>.Task属性的值返回给调用者.

(假设您可以创建单独的实例MyNativeLibrary- 如果您只能在整个应用程序中创建单个实例,则会变得更加困难.)