Хороший вопрос.
Видите ли, при использовании foreach () для итерации получаемого IEnumerable вы в безопасности. Приведенный ниже Enumerator сам реализует IDisposable, который вызывается в случае foreach (даже если цикл завершается с разрывом) и заботится об очистке вашего состояния в GetFileData.
Но если вы будете играть с Enumerator.MoveNextнепосредственно, у вас проблемы, и Dispose никогда не будет вызван, если выйти раньше (конечно, если вы завершите ручную итерацию, это будет). Для ручной итерации, основанной на перечислителе, вы также можете рассмотреть возможность размещения перечислителя в операторе using(как указано в приведенном ниже коде).
Надеюсь, что этот пример с различными вариантами использования предоставит вам некоторую обратную связь по вашему вопросу.
static void Main(string[] args)
{
// Dispose will be called
foreach(var value in GetEnumerable())
{
Console.WriteLine(value);
break;
}
try
{
// Dispose will be called even here
foreach (var value in GetEnumerable())
{
Console.WriteLine(value);
throw new Exception();
}
}
catch // Lame
{
}
// Dispose will not be called
var enumerator = GetEnumerable().GetEnumerator();
// But if enumerator and this logic is placed inside the "using" block,
// like this: using(var enumerator = GetEnumerable().GetEnumerable(){}), it will be.
while(enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
break;
}
Console.WriteLine("{0}Here we'll see dispose on completion of manual enumeration.{0}", Environment.NewLine);
// Dispose will be called: ended enumeration
var enumerator2 = GetEnumerable().GetEnumerator();
while (enumerator2.MoveNext())
{
Console.WriteLine(enumerator2.Current);
}
}
static IEnumerable<string> GetEnumerable()
{
using (new MyDisposer())
{
yield return "First";
yield return "Second";
}
Console.WriteLine("Done with execution");
}
public class MyDisposer : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed");
}
}
Изначально наблюдал: https://blogs.msdn.microsoft.com/dancre/2008/03/15/yield-and-usings-your-dispose-may-not-be-called/
Авторназывает это (тот факт, что ручные MoveNext () и ранний разрыв не будут вызывать Dipose ()) как «ошибку», но это подразумеваемая реализация.