I have simple code with intent to return enumerator over a file or a database if a file does not exist or the version of the data in it is out of date. Simple. However I am struggling with how the method works. Here is why:
public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
try
{
return GetFromFiles(); // returns instantly here
}
catch (ArgumentOutOfRangeException)
{
return GetFromBackingStore();
}
catch (FileNotFoundException)
{
return GetFromBackingStore();
}
}
And GetFromFiles looks like this:
private IEnumerator<KeyValuePair<TKey, TValue>> GetFromFiles()
{
foreach (var path in _paths)
{
using (var fs = _versioner.TryOpen(path))
{
var reader = _serializerFactory.CreateSerializer(fs, FileAccess.Read);
KeyValuePair<TKey, TValue> pair;
while (reader.Read(out pair))
{
yield return pair;
}
}
}
}
Now the problem is that when GetEnumerator is called using foreach, it returns instantly without executing GetFromFiles call first but then it does comeback to GetFromFiles method but now try-catch is already not in play so if TryOpen throws, exception is not handled. I am trying to understand why and how to work around it. It must be related to yield return I think and if this is the case, is there a way to do the trick?
The first method you have, which includes the try/catch returns a reference to the second method, so it’s only called once. Each time the outer code enumerate through, it doesn’t call the first method again because it already have a reference to the enumerator, and therefore it calls it directly. You need to either move the try/catch to the second method, or use a standard collection that gets all initialized once instead of reading the elements one at a time. Hope this helps.