Xamarin F# 到 C# 异步调用:正确的现代方式
Xamarin F# to C# async call: correct modern way
我正在尝试从 Xamarin 中的 F# 代码同步调用 C# 异步函数。原始 C# 代码如下所示(这是 Plugin.BLE 目标设备服务发现):
{
var service = await _connectedDevice.GetServiceAsync(serviceGuid);
if (service != null)
{
var characteristic = await service.GetCharacteristicAsync(Guid.Parse(characteristicGuid);
if (characteristic != null)
{
如果我使用推荐的 F# 原语
module Async =
let inline AwaitPlainTask (task: Task) =
task.ContinueWith(fun t -> ())
|> Async.AwaitTask
以下代码:
async {
let! service = Async.AwaitPlainTask <|d.GetServiceAsync(serviceGuid)
let! characteristic = Async.AwaitPlainTask <|service.GetCharacteristicAsync(characteristicGuid)
...
无法编译,因为“字段、构造函数或成员 'GetCharacteristicAsync' 未定义。”。似乎 F# 在 AwaitPlainTask 之后无法正确确定或转换类型。
如果我尝试以常规方式进行操作,例如:
async {
let serviceTask = d.GetServiceAsync(serviceGuid)
serviceTask.Wait()
let tsk = serviceTask.Result.ToString()
serviceTask.Result 始终为空。
的结果相同(null)
let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
也是。 C# 代码正在运行,所以基本上问题在于如何从 F# 调用它。我想我在这里误解了什么,处理这种结构的正确方法是什么?
P.S.: 这不是 问题的重复,因为它可能已经过时并且不再有效(至少在 Xamarin 上)。
您在此处使用的函数 AwaitPlainTask
,看起来是处理 Task
的便利函数。但是,在 F# async
计算表达式中有一种处理 Task<T>
的标准方法。您应该使用 Async.AwaitTask
和 let!
,如下所示:
async {
// Removing back-pipes as they are often more confusing that useful
let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
let! characteristic = service.GetCharacteristicAsync(characteristicGuid) |> Async.AwaitTask
...
否则你所做的是 C# 中将 Task<T>
转换为 Task
并尝试等待它的等价物:
var result = await ((Task)d.GetServiceAsync(serviceGuid))
这不会在 C# 中编译,但是在 F# 中我们有 unit
类型,这实际上非常方便,因为您不需要类型的扩散和泛型代码的重载。
还值得注意的是,您可能可以删除 AwaitPlainTask
,因为它现在是自 F# 4.0 以来 Async.AwaitTask
中构建的重载,例如:
let waitOneSecond () = async {
// Use do! not let! when you aren't using the result
do! Task.Delay(1000) |> Async.AwaitTask
}
我看了Plugin.BLE
好像waitng GetServiceAsync
可以return null
就像你说的,这是库的设计。
为了处理这个问题,我们可以将结果包装在 Option
类型中,并将其用作之后访问它的安全方式。例如:
let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
let service = service |> Option.ofObj
match service with
| Some s -> let! characteristic = s.GetCharacteristicAsync(characteristicGuid) |> Async.AwaitTask
()
| None -> ()
我正在尝试从 Xamarin 中的 F# 代码同步调用 C# 异步函数。原始 C# 代码如下所示(这是 Plugin.BLE 目标设备服务发现):
{
var service = await _connectedDevice.GetServiceAsync(serviceGuid);
if (service != null)
{
var characteristic = await service.GetCharacteristicAsync(Guid.Parse(characteristicGuid);
if (characteristic != null)
{
如果我使用推荐的 F# 原语
module Async =
let inline AwaitPlainTask (task: Task) =
task.ContinueWith(fun t -> ())
|> Async.AwaitTask
以下代码:
async {
let! service = Async.AwaitPlainTask <|d.GetServiceAsync(serviceGuid)
let! characteristic = Async.AwaitPlainTask <|service.GetCharacteristicAsync(characteristicGuid)
...
无法编译,因为“字段、构造函数或成员 'GetCharacteristicAsync' 未定义。”。似乎 F# 在 AwaitPlainTask 之后无法正确确定或转换类型。
如果我尝试以常规方式进行操作,例如:
async {
let serviceTask = d.GetServiceAsync(serviceGuid)
serviceTask.Wait()
let tsk = serviceTask.Result.ToString()
serviceTask.Result 始终为空。
的结果相同(null)let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
也是。 C# 代码正在运行,所以基本上问题在于如何从 F# 调用它。我想我在这里误解了什么,处理这种结构的正确方法是什么?
P.S.: 这不是
您在此处使用的函数 AwaitPlainTask
,看起来是处理 Task
的便利函数。但是,在 F# async
计算表达式中有一种处理 Task<T>
的标准方法。您应该使用 Async.AwaitTask
和 let!
,如下所示:
async {
// Removing back-pipes as they are often more confusing that useful
let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
let! characteristic = service.GetCharacteristicAsync(characteristicGuid) |> Async.AwaitTask
...
否则你所做的是 C# 中将 Task<T>
转换为 Task
并尝试等待它的等价物:
var result = await ((Task)d.GetServiceAsync(serviceGuid))
这不会在 C# 中编译,但是在 F# 中我们有 unit
类型,这实际上非常方便,因为您不需要类型的扩散和泛型代码的重载。
还值得注意的是,您可能可以删除 AwaitPlainTask
,因为它现在是自 F# 4.0 以来 Async.AwaitTask
中构建的重载,例如:
let waitOneSecond () = async {
// Use do! not let! when you aren't using the result
do! Task.Delay(1000) |> Async.AwaitTask
}
我看了Plugin.BLE
好像waitng GetServiceAsync
可以return null
就像你说的,这是库的设计。
为了处理这个问题,我们可以将结果包装在 Option
类型中,并将其用作之后访问它的安全方式。例如:
let! service = d.GetServiceAsync(serviceGuid) |> Async.AwaitTask
let service = service |> Option.ofObj
match service with
| Some s -> let! characteristic = s.GetCharacteristicAsync(characteristicGuid) |> Async.AwaitTask
()
| None -> ()