gpt4 book ai didi

c# - 在返回 IEnumerable 的方法中使用 IDisposable 对象

转载 作者:IT王子 更新时间:2023-10-29 04:00:08 25 4
gpt4 key购买 nike

假设您有一个内部使用 IDisposable 对象(例如流读取器)的方法,并且 yield 返回从文件中读取的项目。像这样:

public IEnumerable<YourObject> Read(string filename)
{
using(var filestream = new FileStream(filename, FileMode.Open))
{
using(var reader = new StreamReader(filestream))
{
string line;

while((line = reader.ReadLine()) != null)
{
yield return new YourObject(line);
}
}
}
}

当我使用不迭代完整集合的 LINQ 方法时,readerfilestream 是否会被释放?

YourOjbect firstLine = Read("myfile.txt").First();

最佳答案

当您使用yield 关键字时,编译器生成嵌套类,它实现了IEnumerableIEnumeratorIDisposable 并存储所有上下文数据:

[CompilerGenerated]
private sealed class <Read>d__0 : IEnumerable<YourObject>, IEnumerable, IEnumerator<YourObject>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private YourObject <>2__current;
public string <>3__filename;
public Foo <>4__this;
private int <>l__initialThreadId;
public FileStream <filestream>5__1;
public string <line>5__3;
public StreamReader <reader>5__2;
public string filename;

// Methods
[DebuggerHidden]
public <Read>d__0(int <>1__state);
private void <>m__Finally4();
private void <>m__Finally5();
private bool MoveNext();
[DebuggerHidden]
IEnumerator<YourObject> IEnumerable<YourObject>.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose();

// Properties
YourObject IEnumerator<YourObject>.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
}

如您所见,生成方法上下文中的所有局部变量都被移动到这个生成类的字段中。有趣的方法是那些名称中包含 m_Finally 的方法:

private void <>m__Finally4()
{
this.<>1__state = -1;
if (this.<filestream>5__1 != null)
{
this.<filestream>5__1.Dispose();
}
}

如您所见,这些方法处理您的一次性对象(FileStreamStreamReader)。什么时候叫的?在枚举结束时,或者调用 Dispose 时:

private bool MoveNext()
{
bool CS$1$0000;
try
{
int CS$4$0001 = this.<>1__state;
if (CS$4$0001 != 0)
{
if (CS$4$0001 != 3)
{
goto Label_00AB;
}
goto Label_0074;
}
this.<>1__state = -1;
this.<filestream>5__1 = new FileStream(this.filename, FileMode.Open);
this.<>1__state = 1;
this.<reader>5__2 = new StreamReader(this.<filestream>5__1);
this.<>1__state = 2;
while ((this.<line>5__3 = this.<reader>5__2.ReadLine()) != null)
{
this.<>2__current = new YourObject(this.<line>5__3);
this.<>1__state = 3;
return true;
Label_0074:
this.<>1__state = 2;
}
this.<>m__Finally5();
this.<>m__Finally4();
Label_00AB:
CS$1$0000 = false;
}
fault
{
this.System.IDisposable.Dispose();
}
return CS$1$0000;
}

void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
case 2:
case 3:
try
{
switch (this.<>1__state)
{
case 2:
case 3:
break;

default:
break;
}
try
{
}
finally
{
this.<>m__Finally5();
}
}
finally
{
this.<>m__Finally4();
}
break;
}
}

如果您查看 EnumerableFirst() 实现,您会看到 - 它在返回第一个项目后调用 Dispose:

using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
return enumerator.Current;
}
}

因此将调用自动生成类的Dispose,并且所有需要处理的局部变量将通过调用m_Finally 方法来处理。

顺便说一句(与 LINQ 的用法无关)如果您查看 foreach statement implementation您会看到枚举器在枚举后被释放。因此,即使在 break 或异常的情况下,生成类的 Dispose 也会被调用。

关于c# - 在返回 IEnumerable<T> 的方法中使用 IDisposable 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10849643/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com