Don't Fall into the IEnumerable<T> Trap
2 min read
2 min read
Recently I upgraded some code of our company-internal class library and observed a plausible but still tricky problem. See yourself.
public IEnumerable<SomeObject> GetAllValidations()Then, inside another method in some other class there was the following code:
yield return new ValidationObject(...);
public void HandleValidationErrors(IEnumerable<ValidationObject> validationObjects)Now guess what, line 24 gave me an exception when I wanted to access the dictionary with the validationObj as key, telling me that the key was not present. I debugged the code and the obj was present!? My first thought: did someone override the
IDictionary<ValidationObject, IList<ValidationErrors>> cache = new Dictionary<ValidationObject, IList<ValidationErrors>>();
foreach(var validationObj in validationObjects)
//snip snip: If cache not empty, some validation objects haven't been treated/found
public void ActivateValidatorsAccordingToValidationErrors(IEnumerable<ValidationObject> validationObjects, IDictionary<ValidationObject, IList<ValidationErrors>> cache)
foreach(var valObj in validationObjects)
var validator = GetValidatorByValidationObj(valObj);
if(validator != null)
//remove ValidationObj from cache
Equals()??...but that wasn't the case.
IEnumerable<T>. Did you hear about the "lazy-evaluation" of IEnumerables? I did, fortunately. The problem here was that when the code entered the
HandleValidationErrors(...)method, passing in the IEnumerable, that latter has not been evaluated so far. And then I had the situation of a possible multiple enumeration (Resharper tells you that, so don't ignore it!). So what does that mean? When the
IEnumerablewas accessed 1st in line 5, the method
GetAllValidations()was called, actually executing the enumeration with new instances of type ValidationObject. So far so good. Then when the loop in line 18 was executed, the same happened, returning another, new instance of a ValidationObject, basically another
IEnumerable<ValidationObject>than I previously added to the
cachevariable. And here we are, now the exception when accessing the dictionary is quite obvious, isn't it?
var theList = theValidationObjectEnumeration.ToList<ValidationObject>()which "materializes" the list and prevents the multiple enumeration problem.
IEnumerable<T>and if the method returning the IEnumerable was written by some other dev. So watch out!