我在这里使用一个非常简单的示例,以避免与真实案例的细节混淆,并提供一个最小的完整示例。这里的代码可以粘贴到 Visual Studio、Visual Studio Code 或 LinqPad 中,只要你引用了 LanguageExt.Core Nuget 包,并且添加了以下
using
...
using LanguageExt;
using static LanguageExt.Prelude;
想象一下,我有一个 API,它接受
float
,并返回平方根加倍。我有如下辅助函数...
static Either<string, double> Sqrt(double d) =>
d >= 0.0
? Math.Sqrt(d)
: "Error: Negative number";
static Either<string, double> Double(double d) =>
d > 10.0
? "Error: Too big to double"
: 2 * d;
如果成功,我的 API 端点将返回“结果:23.3”形式的字符串,否则返回错误。
我将它们连接在一起,如下...
public static string Calc(double d) =>
(from res1 in Sqrt(d)
from res2 in Double(res1)
select res2)
.Match(res => $"Result: {res}",
err => err);
这按预期工作。显然,我的真实代码会有比这更多的
from
子句,但我试图保持简单。
现在,假设(更现实)辅助函数是
async
,那么我需要修改我的端点,使其看起来像这样......
public static async Task<string> Calc(double d) =>
await (from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.Match(res => $"Result: {res}",
err => err);
再次,一切正常。
旁白: 我从阅读 LanguageExt 问题中 Paul Louth 的一些代码中学到了上面的编码风格。如果有更好的方法,请随时提出。我并不声称自己是这方面的专家。
但是,现在如果出现错误,我想记录它们、发送电子邮件等,因此
Match
方法的第二个 lambda 需要是 async
。为了避免编译器错误,我还需要制作第一个async
,然后await
结果...
public static async Task<string> CalcAsync2(double d) =>
await await (from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.Match(async res => $"Result: {res}",
async err => {
// Log the error, send an email, etc (async);
return err;
});
这开始看起来像是我完全错误地做了这件事。也许我错了,但等待等待的任务似乎并不正确。
有人可以在这里发表评论吗?如果任何辅助方法返回
Left
,我希望能够记录错误,但如果所有 async
工作(实际上,尽可能多的工作)都在 的序列中,我会更喜欢它from
线索,将 lambda 留在 Math
中,除了返回值之外几乎没有什么作用。
只需使用
MatchAsync
:
static Task<string> CalcAsync2(double d) =>
(from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.MatchAsync(res => Task.FromResult($"Result: {res}"),
async err =>
{
await Task.Delay(1);
// Log the error, send an email, etc (async);
return err;
}
);