Еще одно соображение - если вы используете IEnumerable
метод, реализующий yield
, который внутренне выдает исключение, вы не можете перехватить эту отдельную ошибку и продолжить перечисление - см. Раздел «Обработка исключений» https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
пример:
void Main()
{
// even is okay, odd will cause exception
var operations = new[] { 2, 16, 5 /* ! */, 8, 91 /* ! */ };
var results = process(operations);
var en = results.GetEnumerator();
"Regular Enumeration".Title();
testEnumeration(en);
results = process(operations, ex => log("Handled: {0}", ex.Message));
en = results.GetEnumerator();
"Handled Exceptions".Title();
testEnumeration(en);
results = process(operations, ex => log("Handled+: {0}", ex.Message), true);
en = results.GetEnumerator();
"Handled Exceptions and Continue".Title();
testEnumeration(en);
}
/// run the test and debug results
void testEnumeration(IEnumerator en) {
int successCount = 0, failCount = 0;
bool keepGoing = false;
do {
try {
log("==={0}===", "before next");
keepGoing = en.MoveNext();
log("==={0}=== (keepGoing={1}, curr={2})", "after next", keepGoing, en.Current);
// did we have anything?
if(keepGoing) {
var curr = en.Current;
log("==={0}===", "after curr");
log("We did it? {0}", curr);
successCount++;
}
}
catch(InvalidOperationException iopex) {
log(iopex.Message);
failCount++;
}
} while(keepGoing);
log("Successes={0}, Fails={1}", successCount, failCount);
}
/// enumerable that will stop completely on errors
IEnumerable<int> process(IEnumerable<int> stuff) {
foreach(var thing in stuff) {
if(thing % 2 == 1) {
throw new InvalidOperationException("Aww, you broked it");
}
yield return thing;
}
}
/// enumerable that can yield from exceptions
IEnumerable<int> process(IEnumerable<int> stuff, Action<Exception> handleException, bool yieldOnExceptions = false) {
bool shouldYield = false;
foreach(var thing in stuff) {
var result = thing;
try {
if(thing % 2 == 1) {
throw new InvalidOperationException("Aww, you broked it");
}
shouldYield = true;
}
catch(Exception ex) {
handleException(ex);
// `yield break` to stop loop
shouldYield = yieldOnExceptions;
if(yieldOnExceptions) result = -1; // so it returns a different result you could interpret differently
}
if(shouldYield) yield return result;
}
}
void log(string message, params object[] tokens) {
Console.WriteLine(message, tokens);
}
приводит к
Regular Enumeration
---------------------------
===before next===
===after next=== (keepGoing=True, curr=2)
===after curr===
We did it? 2
===before next===
===after next=== (keepGoing=True, curr=16)
===after curr===
We did it? 16
===before next===
Aww, you broked it
===before next===
===after next=== (keepGoing=False, curr=16)
Successes=2, Fails=1
Handled Exceptions
--------------------------
===before next===
===after next=== (keepGoing=True, curr=2)
===after curr===
We did it? 2
===before next===
===after next=== (keepGoing=True, curr=16)
===after curr===
We did it? 16
===before next===
Handled: Aww, you broked it
===after next=== (keepGoing=True, curr=8)
===after curr===
We did it? 8
===before next===
Handled: Aww, you broked it
===after next=== (keepGoing=False, curr=8)
Successes=3, Fails=0
Handled Exceptions and Continue
---------------------------------------
===before next===
===after next=== (keepGoing=True, curr=2)
===after curr===
We did it? 2
===before next===
===after next=== (keepGoing=True, curr=16)
===after curr===
We did it? 16
===before next===
Handled+: Aww, you broked it
===after next=== (keepGoing=True, curr=-1)
===after curr===
We did it? -1
===before next===
===after next=== (keepGoing=True, curr=8)
===after curr===
We did it? 8
===before next===
Handled+: Aww, you broked it
===after next=== (keepGoing=True, curr=-1)
===after curr===
We did it? -1
===before next===
===after next=== (keepGoing=False, curr=-1)
Successes=5, Fails=0
Обратите внимание, что Current
счетчика "застрял" в последнем успешномMoveNext
во время «обычного перечисления», тогда как обработанные исключения позволяют завершить цикл.