我们如何在C++/CX中等待C#异步委托函数

Yuc*_*ong 5 c# windows asynchronous c++-cx

这是关于C++(不同平台的共享代码)与C#(Windows Universal App)之间的通信.我们知道,下面是我们如何从一个函数调用C++C#.

C#

class FooCS
{
    FooCS()
    {
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEvent;
        // Some other stuff ...
    }

    string GetSomeValueEvent()
    {
        // Some other stuff ... 
        return "Hello World"; 
    }
}
Run Code Online (Sandbox Code Playgroud)

C++

public delegate Platform::String GetSomeValueEventHandler(); 

class FooC
{
    public:
    event GetSomeValueEventHandler^ m_GetSomeValueEvent; 
    void someFunction(){
        Platform::String^ str = m_GetSomeValueEvent();
        // Some other stuff ... 
    }
}
Run Code Online (Sandbox Code Playgroud)

以上是非常直截了当的.但问题是这个字符串GetSomeValueEvent()C#做一些繁重的任务,比如从数据库中读取数据,我必须做到async.

    async Task<string> GetSomeValueEvent() {
        // Some other stuff ... 
        return "Hello World"; 
    }
Run Code Online (Sandbox Code Playgroud)

现在问题是:如何使我的C++代码等待返回此委托函数?换句话说,应该将以下签名修改为什么?

    public delegate Platform::String GetSomeValueEventHandler(); 
Run Code Online (Sandbox Code Playgroud)

我一直在谷歌搜索,无法找到这个问题的正确术语.如果你肯定知道这是不可能的,那么至少要知道.


最终,这就是我们所做的:

    string GetSomeValueEvent() {
        // Convert a async method to non-async one
        var val = someAsyncMethod().Result; 
        // Some other stuff ... 
        return "Hello World"; 
    }
Run Code Online (Sandbox Code Playgroud)

您必须确保GetSomeValueEvent未在主线程上运行以防止死锁.

Eld*_*iev 3

UPD:从事件返回值是一个非常糟糕的主意。考虑其他选项,例如某种可观察模式,但只有一个观察者。

正如 @RaymondChen 提到的,对于异步事件,最好使用延迟模式。这是 Windows 运行时中常用的模式。

将该GetDeferral()方法添加到事件参数中,该参数返回一个特殊的延迟对象。该对象必须具有Complete()通知您的代码异步操作已完成的方法。

=================================================== =========================

async关键字实际上不会更改函数原型。因此,您只需更改委托中的返回值以匹配原始值 ( Task<string>)。

但有一个问题:Windows 运行时没有类型Task<>,它是 TPL 的一部分,TPL 是一个 .NET 库。相反,Windows 运行时使用IAsyncOperation<>IAsyncAction来表示异步操作。

所以这是你需要做的:

  1. 修改委托人签名:

    public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用AsAsyncOperation()扩展方法转换Task<>IAsyncOperation<>

    async Task<string> GetSomeValueEvent()
    {
        // Some other stuff ... 
        return "Hello World"; 
    }
    
    IAsyncOperation<string> GetSomeValueEventWrapper()
    {
        return GetSomeValueEvent().AsAsyncOperation();
    }
    
    FooCS()
    {
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEventWrapper;
        // Some other stuff ...
    }
    
    Run Code Online (Sandbox Code Playgroud)