我正在尝试围绕用本机代码编写的第三方库编写一个c#包装器,以便在我们的应用程序中使用,这些应用程序几乎都是用.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&lt; T&gt;的工作方面有更多经验的人们联系。对象和async / await模式比我做的。
答案 0 :(得分:9)
您可以使用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
有效负载,您可以将其设置为每次调用的唯一值,以便您可以区分。
答案 1 :(得分:6)
听起来你正在寻找TaskCompletionSource<T>
。您可以通过创建TaskCompletionSource
来包装库,创建MyNativeLibrary
的实例并注册设置任务完成源结果的回调,然后从同一实例请求数据。如果其中任何一个步骤失败,请在任务完成源上设置错误。然后只需将TaskCompletionSource<>.Task
属性的值返回给调用者。
(假设您可以创建MyNativeLibrary
的单独实例 - 如果您只能在整个应用中创建单个实例,则会变得更加困难。)