在将值附加到现有 IEnumerable 的末尾时使用 yield return。

6

我刚刚了解到yield return,它看起来非常不错。我在一个方法中使用它,就像这样:

public IEnumerable<ValidationResult> Validate(ValidationContext vc)
{
    if (Name == "Arbitary")
        yield return new ValidationResult("Bad Name.", new[] { "Name" });
    else if (Email == "BadEmail")
        yield return new ValidationResult("Bad Email.", new [] {"Email" });
    // further rules follow.
}

然而,我需要修改这个方法,让它从子方法中返回一些验证结果。如果不使用yield,代码看起来会像这样:

public override IEnumerable<ValidationResult> Validate(ValidationContext vc)
{
    // TryValidateObject fills the ICollection you pass it.
    List<ValidationResult> retVal = new List<ValidationResult>();
    Validator.TryValidateObject(this, vc, retVal, true);

    if (Name == "Arbitary")
        retVal.Add(new ValidationResult("Bad Name.", new[] { "Name" }));
    else if (Email == "BadEmail")
        retVal.Add(new ValidationResult("Bad Email.", new[] { "Email" }));

    return retVal;
}

是否可以使用yield重写这个代码?


请注意,您拼错了“arbitrary”这个单词。 - Eric Lippert
@EricLippert 哈哈,确实。我经常拼错这个单词。不过这只是一个示例验证规则,因为我还没想好“真正”的规则。 - Oliver
3个回答

4

你是在寻找这个吗?

public override IEnumerable<ValidationResult> Validate(ValidationContext vc)
{
    // TryValidateObject fills the ICollection you pass it.
    List<ValidationResult> retVal = new List<ValidationResult>();
    Validator.TryValidateObject(this, vc, retVal, true);
    foreach (var item in retVal)
        yield return item;
    if (Name == "Arbitary")
        yield return new ValidationResult("Bad Name.", new[] { "Name" });
    else if (Email == "BadEmail")
        yield return new ValidationResult("Bad Email.", new[] { "Email" });       
}

如果是这样,我相信第一个版本看起来更好。

谢谢。yield break 是必要的吗? - Oliver

3

到目前为止,其他已发布的解决方案都很好。这里有另一种解决问题的方法:

  • 创建两个序列
  • 将它们连接起来
  • 返回连接结果。

所以:

public override IEnumerable<ValidationResult> Validate(ValidationContext vc)
{
  return ResultsFromValidator(vc).Concat(AdditionalResults());
}
private IEnumerable<ValidationResult> ResultsFromValidator(ValidationContext vc)
{
  List<ValidationResult> retVal = new List<ValidationResult>();
  Validator.TryValidateObject(this, vc, retVal, true);
  return retVal;
}
private IEnumerable<ValidationResult> AdditionalResults()
{
  if (Name == "Arbitrary")
    yield return new ValidationResult("Bad Name.", new[] { "Name" });
  ...
}

2
当然,只需使用以下内容:
foreach (var item in retVal) {
    yield return item;
}

之后,你还可以继续使用yield returns。所以在调用方法并返回retVal后,可以像第一个示例中那样继续执行。就像这样:

public override IEnumerable<ValidationResult> Validate(ValidationContext vc)
{
    List<ValidationResult> retVal = new List<ValidationResult>();
    Validator.TryValidateObject(this, vc, retVal, true);
    foreach (var item in retVal) {
        yield return item;
    }

    if (Name == "Arbitary")
        yield return new ValidationResult("Bad Name.", new[] { "Name" });
    else if (Email == "BadEmail")
        yield return new ValidationResult("Bad Email.", new[] { "Email" });
    //...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接