这个问题结合了两个我不完全理解的主题
在F#中通过paper关于async的内容,我遇到了Agent / MailboxProcessors的主题,它可以用来实现被动状态机。是否可以使用C#5中新的异步/等待功能来实现C#中类似的功能,或者是否已经有更适合的模拟?
答案 0 :(得分:11)
有一点可怕的黑客攻击,你可以使用MailboxProcessor
来自C#的async
类型。一些困难是该类型使用一些F#特定功能(可选参数是选项,函数是FSharpFunc
类型等。)
从技术上讲,最大的区别是F#async被分析,而C#async创建一个已经运行的任务。这意味着要从C#构造F#异步,您需要编写一个采用unt -> Task<T>
并创建Async<T>
的方法。我写了blog post that discusses the difference。
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();
}
正如你所看到的,这看起来非常糟糕:-)。
原则上,可以为MailboxProcessor
类型编写一个C#友好包装器(只从这段代码中提取丑陋的位),但是存在一些问题。
在F#中,您经常使用tail-recursive asyncs来实现邮箱处理器中的状态机。如果你在C#中编写相同的东西,你最终会获得StackOverflow
,所以你需要编写具有可变状态的循环。
完全可以在F#中编写代理并从C#中调用它。这只是从F#公开C#友好界面(使用Async.StartAsTask
方法)。
答案 1 :(得分:3)
原则上,我希望将这些F#API转换为C#-plus-async-await是直截了当的。
在实践中,我不清楚它是否会变得漂亮,或者丑陋且充满额外的类型注释,或者仅仅是非惯用的,并且需要一些API按摩来使它在C#中更有家的感觉。我认为陪审团已经结束,直到有人做了工作并尝试了。 (我认为在等待CTP中没有这样的样本。)
答案 2 :(得分:0)
你可能会看一看Stact。它在一段时间内没有更新,但如果你想用更好的C#支持做一些事情,你可能会发现它是一个很好的起点。不过,我不认为它与async / await是最新的。