Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

yield return when appending values on to the end of an existing IEnumerable

I just found out about yield return, I seems really nice. I use it in a method like this:

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.
}

However, I need to change this method to return some ValidationResults from a child method. Without using yield, the code would look like this:

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;
}

Is it possible to rewrite this using yield?

like image 296
Oliver Avatar asked Apr 11 '12 11:04

Oliver


2 Answers

Are you looking for this?

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" });       
}

If so, I believe the first version looks nicer.

like image 64
Adrian Iftode Avatar answered Sep 19 '22 12:09

Adrian Iftode


The other solutions posted so far are good. Here's another way to solve your problem:

  • Create two sequences
  • concatenate them
  • return the concatenation.

So:

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" });
  ...
}
like image 25
Eric Lippert Avatar answered Sep 21 '22 12:09

Eric Lippert