C# 使用任务包装本机异步函数<;T>;

C# 使用任务包装本机异步函数<;T>;,c#,task-parallel-library,pinvoke,native,async-await,C#,Task Parallel Library,Pinvoke,Native,Async Await,我正试图围绕一个第三方库编写一个c#包装器,该库是用本机代码编写的,用于我们的应用程序中,这些应用程序几乎完全是用.NET编写的,我正试图忠实于c#模式。这个库中几乎所有的调用本质上都是异步的,将所有异步调用封装到任务对象中似乎是合适的。下面是一个关于本机库结构的过于简化的示例: delegate void MyCallback(string outputData); class MyNativeLibrary { public int RegisterCallback(MyCallb

我正试图围绕一个第三方库编写一个c#包装器,该库是用本机代码编写的,用于我们的应用程序中,这些应用程序几乎完全是用.NET编写的,我正试图忠实于c#模式。这个库中几乎所有的调用本质上都是异步的,将所有异步调用封装到任务对象中似乎是合适的。下面是一个关于本机库结构的过于简化的示例:

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);
}
class WrapperRoundNativeCode
{
公共异步任务请求数据(字符串输入数据);
}

到目前为止,我还没有找到一种合适的方法来实现这一点,我正在接触那些在使用任务对象和异步/等待模式方面比我更有经验的人

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

(这是假设您可以创建MyNativeLibrary的单独实例-如果您只能在整个应用程序中创建一个实例,那么这将变得非常困难。)

您可以使用一个实例来实现此目的。符合以下代码的内容:

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;
    }
}
class WrapperRoundNativeCode
{
公共异步任务请求数据(字符串输入数据)
{
var completionSource=new TaskCompletionSource();
var result=Native.RegisterCallback(s=>completionSource.SetResult);
如果(结果==-1)
{
SetException(newsomeException(“设置回调失败”);
返回completionSource.Task;
}
结果=本机.RequestData(inputData);
如果(结果==-1)
SetException(新的SomeException(“请求数据失败”);
返回completionSource.Task;
}
}

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

如何知道是哪个
RequestData
导致了
MyCallback
请求?如何获得异常通知?如何取消注册回调?还是自动注销?@luiscubal,实际上有几种方法可以注册对这个第三方库的回调。我在这里工作了几年就掌握了这些技术@我的观点是没有办法检测异步错误。也就是说,你注册回叫很好,你的请求通过了,但是在处理请求时出现了一些错误;没有办法通知你。谢谢,这很有效。我还有一个问题:
Task
具有通用和非通用的风格,但我只看到了
TaskCompletionSource
的通用版本。我错过了吗?@Joey Herrington
Task
扩展了
Task
,因此有一个从
Task
Task
的隐式转换。您只需构建一个
TaskCompletionSource
并使用
SetResult(null)
(或者任何虚拟值,而不是
null
)。是的,我就是这么做的。我只是不知道是否有真正的非泛型类型。看起来你搞定了!我尝试过这种技术,它似乎工作得很好。谢谢你的帮助对不起,我只能将一个答案标记为正确,否则,我会将你和Jon Skeet标记为正确。