这是关于绑定返回不同类型的单子的a similar one I asked个后续问题.在得到一个明确的答案后,我意识到我没有问完整的问题.马克·西曼(回答者)建议我把这个问题作为一个新问题来问,而不是修改这个问题(这个问题本身就存在,所以值得留下来).

为了简单起见,我正在介绍一个不太现实的用例(例如角色判断可以以不同的方式完成,等等),但我尽量不混淆这个问题,所以请耐心听我说.

假设我想写一个接受int的方法,并且需要...

  1. 判断所使用的授权人是否处于发出请求的适当角色
  2. 判断数据库中与客户对应的Id
  3. 判断客户是否处于活动状态

如果我们完成了所有这些任务,我们将返回客户,否则我们将返回错误消息.所有方法均为async.

我有以下(简化的)方法...

async static Task<Either<string, int>> CheckUser(int id) {
  // Check the authed user is in the right role, etc. For simplicity, we'll just branch on the id
  // Simulate some async work
  await Task.Delay(0);
  if (id < 0) {
    return "Invalid";
  }
  return id;
}

async static Task<Option<Customer>> Exists(int id) {
  // Check the customer id refers to a real customer. Simulate some async work
  await Task.Delay(0);
  return id < 10 ? None : new Customer(id, "Jim Spriggs");
}

async static Task<Either<string, Customer>> IsActive(Customer c) {
  // Simulate some async work
  await Task.Delay(0);
  if (c.Id % 2 == 0) {
    return "Inactive";
  }
  return c;
}

record Customer(int Id, string Name);

我想把这些结合在一起,如下所示(实际上,我要做的不仅仅是将结果写入控制台,但你明白了)...

await CheckUser(31)
  .Bind(async id => (await Exists(id)).ToEither("No such customer"))
  .Bind(IsActive)
  .Match(n => Console.WriteLine($"Success: {n}"), ex => Console.WriteLine($"Ex: {ex}"));

然而,我在第二行的id参数到Exists参数上得到了一个编译器错误..."CS1503 Argument 1: cannot convert from 'LanguageExt.Either<string, int>' to 'int'"

我试过有await/async个关键字和没有await/async个关键字,但还是无法编译.我不确定是否需要将它们添加到lambda中.

有人能解释我是怎么做到的吗?谢谢

推荐答案

你遇到问题的原因不仅仅是is Either a monad,异步计算(任务)are too.因此,您试图调用的Bind方法与Task<T>相关联,而不是Either<L, R>.

您还可以看到Bind方法中id的推断类型是Either<string, int>而不是int.

处理此类问题最方便的方法是将单子(Task<Either<L, R>>)的"堆栈"视为"组合"单子.LanguageExt附带了这样一个现成的类型:EitherAsync.

可以使用ToAsync方法将Task<Either<L, R>>值转换为EitherAsyn<L, R>值:

[Theory]
[InlineData(-1, "Ex: Invalid")]
[InlineData( 9, "Ex: Unknown customer")]
[InlineData(36, "Ex: Inactive")]
public async Task Answer(int id, string expected)
{
    var actual = await CheckUser(id).ToAsync()
        .Bind(i => Exists(i).ToEitherAsync("Unknown customer"))
        .Bind(c => IsActive(c).ToAsync())
        .Match(n => $"Success: {n}", ex => $"Ex: {ex}");
    Assert.Equal(expected, actual);
}

以上所有测试用例均通过.

请注意,您必须将Task<Either<L, R>>个值中的每个值转换为EitherAsync<L, R>ToAsync(),将Task<Option<Customer>>个值转换为EitherAsync<string, Customer>ToEitherAsync.这是一个仪式,你必须通过保持方法(CheckUserExistsIsActive)"干净".

或者,您可以更改方法以返回EitherAsync个值.

Csharp相关问答推荐

Plotly.NET访问互联网时出现异常

如何禁用ASP.NET MVP按钮,以便无法使用开发人员控制台重新启用它

如何在Visual Studio中为C# spread操作符设置格式规则?

AutoMapper -如何为两个不同的用例设置单个映射?

Elasticsearch:当我try 使用c#将嵌套对象添加到filter中时出现问题

当我使用NET6作为目标框架时,为什么DotNet使用NET8作为MS包?

无法通过绑定禁用条目

如何将MongoDB序列化程序设置为内部对象属性

Automapper 12.x将GUID映射到字符串

如何比较C#中的L和ł(波兰字符)返回TRUE

在implementationFactory中避免循环依赖

如何在Akka.NET中重新启动执行元时清除邮箱

.NET8->;并发词典总是比普通词典快...怎么回事?[包含基准结果和代码]

未在Windows上运行的Maui项目

如何使用类似于[SELECT*FROM&Q;&Q;WHERE&Q;]SQL查询的System.Data.Entity创建查询?

DropDownListFor未显示选定值

如何在C# WinForm控件中使用Windows 10/11的黑暗主题?

.NET文档对继承的困惑

如何对构建在Clean架构和CQRS之上的控制器进行单元测试?

Avalonia MVVM数据模板