我们如何在 C++/CX 中等待 C# 异步委托函数
How do we await for C# async delegate function in C++/CX
这是关于C++
(不同平台的共享代码)与C#
(Windows通用应用程序)之间的通信。我们知道下面是我们如何进行从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";
}
}
C++
public delegate Platform::String GetSomeValueEventHandler();
class FooC
{
public:
event GetSomeValueEventHandler^ m_GetSomeValueEvent;
void someFunction(){
Platform::String^ str = m_GetSomeValueEvent();
// Some other stuff ...
}
}
以上内容非常简单明了。但问题是C#
中的这个字符串GetSomeValueEvent()
做一些繁重的工作,比如从数据库读取数据,我不得不把它弄成async
。
async Task<string> GetSomeValueEvent() {
// Some other stuff ...
return "Hello World";
}
现在问题来了:如何让我的C++
代码等待这个委托函数的return?也就是说,下面的签名应该修改成什么?
public delegate Platform::String GetSomeValueEventHandler();
我一直在谷歌上搜索,找不到解决这个问题的正确术语。如果您确定这是不可能的,至少很高兴知道。
最终,这就是我们所做的:
string GetSomeValueEvent() {
// Convert a async method to non-async one
var val = someAsyncMethod().Result;
// Some other stuff ...
return "Hello World";
}
您必须确保 GetSomeValueEvent
不是 运行 在主线程上以防止死锁。
UPD:从事件返回一个值是一个非常糟糕的主意。考虑其他选项,例如某种可观察模式,但只有一个观察者。
正如@RaymondChen 提到的,对于异步事件,最好使用延迟模式。这是 Windows 运行时常用的模式。
将 GetDeferral()
方法添加到您的事件参数中,它 return 是一个特殊的延迟对象。此对象必须具有 Complete()
方法,该方法通知您的代码异步操作已完成。
============================================= ==============================
async
关键字实际上不会更改函数原型。因此,您只需更改委托中的 return 值以匹配原始值 (Task<string>
)。
但有一个问题:Windows 运行时没有 Task<>
类型,它是 TPL 的一部分,它是一个 .NET 库。相反,Windows 运行时使用 IAsyncOperation<>
和 IAsyncAction
来表示异步操作。
所以这是你需要做的:
更改委托人签名:
public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler();
使用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 ...
}
我会做的是使用 c++ 等价物:
var result = GetSomeValueEvent().GetAwaiter().GetResult();
这不仅会为您获取结果,还会传递该任务中发生的所有异常。
这是关于C++
(不同平台的共享代码)与C#
(Windows通用应用程序)之间的通信。我们知道下面是我们如何进行从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";
}
}
C++
public delegate Platform::String GetSomeValueEventHandler();
class FooC
{
public:
event GetSomeValueEventHandler^ m_GetSomeValueEvent;
void someFunction(){
Platform::String^ str = m_GetSomeValueEvent();
// Some other stuff ...
}
}
以上内容非常简单明了。但问题是C#
中的这个字符串GetSomeValueEvent()
做一些繁重的工作,比如从数据库读取数据,我不得不把它弄成async
。
async Task<string> GetSomeValueEvent() {
// Some other stuff ...
return "Hello World";
}
现在问题来了:如何让我的C++
代码等待这个委托函数的return?也就是说,下面的签名应该修改成什么?
public delegate Platform::String GetSomeValueEventHandler();
我一直在谷歌上搜索,找不到解决这个问题的正确术语。如果您确定这是不可能的,至少很高兴知道。
最终,这就是我们所做的:
string GetSomeValueEvent() {
// Convert a async method to non-async one
var val = someAsyncMethod().Result;
// Some other stuff ...
return "Hello World";
}
您必须确保 GetSomeValueEvent
不是 运行 在主线程上以防止死锁。
UPD:从事件返回一个值是一个非常糟糕的主意。考虑其他选项,例如某种可观察模式,但只有一个观察者。
正如@RaymondChen 提到的,对于异步事件,最好使用延迟模式。这是 Windows 运行时常用的模式。
将 GetDeferral()
方法添加到您的事件参数中,它 return 是一个特殊的延迟对象。此对象必须具有 Complete()
方法,该方法通知您的代码异步操作已完成。
============================================= ==============================
async
关键字实际上不会更改函数原型。因此,您只需更改委托中的 return 值以匹配原始值 (Task<string>
)。
但有一个问题:Windows 运行时没有 Task<>
类型,它是 TPL 的一部分,它是一个 .NET 库。相反,Windows 运行时使用 IAsyncOperation<>
和 IAsyncAction
来表示异步操作。
所以这是你需要做的:
更改委托人签名:
public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler();
使用
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 ... }
我会做的是使用 c++ 等价物:
var result = GetSomeValueEvent().GetAwaiter().GetResult();
这不仅会为您获取结果,还会传递该任务中发生的所有异常。