5

我正在尝试围绕用本机代码编写的第三方库编写 ac# 包装器,以便在我们的应用程序中使用,这些库几乎完全用 .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
}

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

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData);
}

到目前为止,我还没有找到合适的方法来实现这一点,我正在联系那些在使用 Task<T> 对象和异步/等待模式方面比我更有经验的人。

4

2 回答 2

11

你会为此使用 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;
    }
}

这个答案假设不会有对这个方法的并发调用。如果有,您将需要某种方法来区分不同的调用。许多 API 提供了一个userData有效负载,您可以将其设置为每次调用的唯一值,以便您可以区分。

于 2013-10-21T13:47:08.097 回答
8

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

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

于 2013-10-21T13:46:50.437 回答