我希望能够从F#中使用C#库.大多数情况下,这非常简单.但是,如果我尝试调用返回的函数,则Task
无法获取返回值.
所以,我有C#方法,其定义如下:
public async TaskReadEventAsync (string streamName, int position) where TEvent: class
我试图从F#中使用此方法,如下所示:
let readEventFromEventStore<'a when 'a : not struct> (eventStore:IEventStoreRepository) (streamName:string) (position:int) =
async {
return eventStore.ReadEventAsync(streamName, position)
|> Async.AwaitTask
}
我部分地应用此函数与an的实例IEventStoreRepository
和我希望从以下位置检索事件的流名称:
let readEvent = readEventFromEventStore eventStore streamName
然后,最后,我应用剩余的参数:
let event = readEvent StreamPosition.Start
当我得到的值event
是一个FSharpAsync
,而不是T
从Task
我的预期.
调用async
C#编写的方法的F#中的正确方法是什么,返回类型为?Task
并且访问T
?的值?
首先,在您的用例中,不需要async { }
块.Async.AwaitTask
返回一个Async<'T>
,所以你的async { }
块只是打开它Async
你得到对象并立即重新包装它.
现在我们已经摆脱了不必要的async
阻止,让我们来看看你得到的类型,以及你想要获得的类型.你有一个Async<'a>
,你想要一个类型的对象'a
.查看可用的Async
函数,具有类型签名的那个Async<'a> -> 'a
是Async.RunSynchronously
.它需要两个可选参数,a int
和a CancellationToken
,但是如果你把它们留下来,你就得到了你正在寻找的功能签名.当然,一旦你看到文档就会发现它Async.RunSynchronously
就是这样相当于C#的F# 和C#一样(但不完全一样)await
,这就是你想要的.await
.C#await
是一个可以在async
函数内部使用的语句,而F#使用Async.RunSynchronously
一个async
对象阻塞当前线程,直到该async
对象完成运行.在这种情况下,这正是您正在寻找的.
let readEventFromEventStore<'a when 'a : not struct> (eventStore:IEventStoreRepository) (streamName:string) (position:int) =
eventStore.ReadEventAsync(streamName, position)
|> Async.AwaitTask
|> Async.RunSynchronously
这应该可以帮到你找到你想要的东西.并注意找出所需函数的函数签名的技术,然后查找具有该签名的函数.它将来会有很多帮助.
更新:感谢您Tarmil在意见指出我的错误:Async.RunSynchronously
是不是等同于C#的await
.它非常相似,但是由于RunSynchronously
阻止了当前线程,因此需要注意一些重要的细微之处.(您不想在GUI线程中调用它.)
更新2:如果要在不阻塞当前线程的情况下等待异步结果,它通常是模式的一部分,如下所示:
调用一些异步操作
等待结果
做一些结果
编写该模式的最佳方法如下:
let equivalentOfAwait () = async { let! result = someAsyncOperation() doSomethingWith result }
上面假设doSomethingWith
返回unit
,因为你是因为它的副作用而调用它.如果它返回一个值,你会做:
let equivalentOfAwait () = async { let! result = someAsyncOperation() let value = someCalculationWith result return value }
或者,当然:
let equivalentOfAwait () = async { let! result = someAsyncOperation() return (someCalculationWith result) }
这假设someCalculationWith
不是异步操作.相反,你需要将两个异步操作链接在一起,其中第二个使用第一个结果 - 或者甚至是某种序列中的三个或四个异步操作 - 那么它看起来像这样:
let equivalentOfAwait () = async { let! result1 = someAsyncOperation() let! result2 = nextOperationWith result1 let! result3 = penultimateOperationWith result2 let! finalResult = finalOperationWith result3 return finalResult }
除了let!
后面的return
内容完全相同return!
,所以写得更好:
let equivalentOfAwait () = async { let! result1 = someAsyncOperation() let! result2 = nextOperationWith result1 let! result3 = penultimateOperationWith result2 return! (finalOperationWith result3) }
所有这些函数都将产生一个Async<'T>
,其中'T
将是async
块中最终函数的返回类型.要真正运行的异步块,你要么Async.RunSynchronously
如已经提到的,或者你可以使用的各种一个Async.Start
功能(Start
,StartImmediate
,StartAsTask
,StartWithContinuations
,等).这个Async.StartImmediate
例子也谈到了这个Async.SwitchToContext
函数,这可能是你想要阅读的内容.但我不太熟悉SynchronizationContext
s告诉你更多.