通过一些非常可怕的黑客攻击,您可以使用MailboxProcessor
C# 中的类型 using async
. 一些困难是该类型使用了一些 F# 特定的功能(可选参数是选项,函数是FSharpFunc
类型等)
从技术上讲,最大的区别是 F# async 被处理,而 C# async 创建一个已经在运行的任务。这意味着要从 C# 构造 F# 异步,您需要编写一个方法来获取unt -> Task<T>
和创建Async<T>
. 我写了一篇博客文章讨论了差异。
Anwyay,如果你想试验,这里有一些你可以使用的代码:
static FSharpAsync<T> CreateAsync<T>(Func<Task<T>> f)
{
return FSharpAsync.FromContinuations<T>(
FuncConvert.ToFSharpFunc<
Tuple< FSharpFunc<T, Unit>,
FSharpFunc<Exception, Unit>,
FSharpFunc<OperationCanceledException, Unit> >>(conts => {
f().ContinueWith(task => {
try { conts.Item1.Invoke(task.Result); }
catch (Exception e) { conts.Item2.Invoke(e); }
});
}));
}
static void MailboxProcessor() {
var body = FuncConvert.ToFSharpFunc<
FSharpMailboxProcessor<int>,
FSharpAsync<Unit>>(mbox =>
CreateAsync<Unit>(async () => {
while (true) {
var msg = await FSharpAsync.StartAsTask
( mbox.Receive(FSharpOption<int>.None),
FSharpOption<TaskCreationOptions>.None,
FSharpOption<CancellationToken>.None );
Console.WriteLine(msg);
}
return null;
}));
var agent = FSharpMailboxProcessor<int>.Start(body,
FSharpOption<CancellationToken>.None);
agent.Post(1);
agent.Post(2);
agent.Post(3);
Console.ReadLine();
}
正如你所看到的,这看起来真的很可怕:-)。
原则上,可以为该类型编写一个 C# 友好的包装器MailboxProcessor
(只需从该代码中提取丑陋的部分),但存在一些问题。
在 F# 中,您经常使用尾递归异步来实现邮箱处理器中的状态机。如果您在 C# 中编写相同的内容,您最终会得到StackOverflow
,因此您需要编写具有可变状态的循环。
完全可以用 F# 编写代理并从 C# 调用它。这只是从 F# 暴露 C# 友好接口的问题(使用Async.StartAsTask
方法)。