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.
Then I immediately saw the problem when looking at the method signature. Did you get it?
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?
Obviously this was a silly mistake and fixing it was quite easy by inserting somewhere (I did it in the first possible place to keep other devs from falling in this trap as well ;)) a line like
var theList = theValidationObjectEnumeration.ToList<ValidationObject>()which "materializes" the list and prevents the multiple enumeration problem.
I'm writing this post because such bugs can become quite nasty to find, especially if you don't know the fact about lazy-loading of
IEnumerable<T>and if the method returning the IEnumerable was written by some other dev. So watch out!