2012-04-11 15 views
5

Ho appena scoperto circa yield return, mi sembra davvero bello. Io lo uso in un metodo come questo:return return quando si aggiungono valori alla fine di un IEnumerable esistente

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

Tuttavia, ho bisogno di cambiare questo metodo per restituire alcuni ValidationResults da un metodo bambino. Senza usare yield, il codice sarebbe simile a questa:

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

E 'possibile riscrivere questo utilizzando yield?

+0

nota che hai scritto male "arbitrario". –

+0

@EricLippert Hah, infatti. Scrivo male tutto il tempo. In questo caso però è solo una regola di validazione di esempio, poiché non ho ancora elaborato i "veri". – Oliver

risposta

3

Stai cercando questo?

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

Se è così, credo che la prima versione sia più bella.

+0

Grazie per questo. È necessaria la "pausa di rendimento"? – Oliver

+0

No, lo cancellerò. –

2

Sicuro. Basta usare questo:

foreach (var item in retVal) { 
    yield return item; 
} 

Successivamente, è anche possibile continuare con i rendimenti di rendimento. Quindi potresti restituire il tuo retVal dopo aver chiamato il metodo e poi continuare come nel tuo primo campione. Come questo:

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

Le altre soluzioni pubblicate finora sono buone. Ecco un altro modo per risolvere il problema:

  • Creare due sequenze
  • concatenare li
  • ritorno la concatenazione.

Quindi:

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