使用C#中的文件时,我习惯于考虑释放相关资源。通常这是一个使用声明,除非它是一个简单的方法 就像File.ReadAllLines一样,它会为我打开和关闭文件。
.Net 4.0引入了便捷方法File.ReadLines。这将返回一个IEnumerable,并被视为一种更有效的方法来处理文件 - 它避免将整个文件存储在内存中。为此,我假设枚举器中有一些延迟执行逻辑。
显然,由于这个方法返回一个IEnumerable,而不是IDisposable,我不能用我对使用语句的直觉反应。
我的问题是: 考虑到这一点,使用此方法是否存在资源释放的问题?
调用此方法是否意味着关联文件锁的释放是不确定的?
答案 0 :(得分:17)
IEnumerable
不会从IDisposable继承,因为通常,实现它的类只会为您提供可枚举的 promise ,它实际上还没有做任何可以处理的事情。
但是,当您枚举它时,首先通过调用IEnumerator
方法检索IEnumerable.GetEnumerator
,通常,您获取的基础对象 实现{{} 1}}。
实施IDisposable
的方式类似于:
foreach
这样,如果对象确实实现了var enumerator = enumerable.GetEnumerator();
try
{
// enumerate
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
disposable.Dispose();
}
,它将被处理掉。对于IDisposable
,在您开始枚举文件之前,该文件并未真正打开,因此您从File.ReadLines
获取的对象不需要处理,但您获得的枚举器确实存在。
正如评论所示,File.ReadLines
不会继承IEnumerator
,即使有许多典型的实现,但通用IDisposable
确实会继承IEnumerator<T>
。
答案 1 :(得分:5)
+1 Lasse的答案。
特别是对于File.ReadLines
,枚举器调用.MoveNext()
时,内部TextReader
会在遇到EOF时被处理掉,或者发生错误。
private bool MoveNext()
{
bool flag;
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>7__wrap2 = this.reader;
this.<>1__state = 1;
this.<line>5__1 = null;
goto Label_005C;
case 2:
this.<>1__state = 1;
goto Label_005C;
default:
goto Label_0078;
}
Label_003E:
this.<>2__current = this.<line>5__1;
this.<>1__state = 2;
return true;
Label_005C:
if ((this.<line>5__1 = this.reader.ReadLine()) != null)
{
goto Label_003E;
}
this.<>m__Finally3(); // Disposal at end of file.
Label_0078:
flag = false;
}
fault
{
this.System.IDisposable.Dispose(); // Disposal due to fault.
}
return flag;
}
答案 2 :(得分:0)
只是为了更新:
在 .net 5 中,它返回的 IEnumerable
确实实现了 IDisposable
,您可能需要在转换后调用 Dispose
。但是如果你读到文件末尾,它会自行处理。