40

IDisposableオブジェクト(たとえば、streamreader)を内部的に使用するメソッドがあり、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メソッドを使用するとreader、とは破棄されますか?filestream

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

3 に答える 3

28

キーワードコンパイラを使用するとyield、ネストされたクラスが生成されます。このクラスは、を実装し、IEnumerableすべてのコンテキストデータを格納します。IEnumeratorIDisposable

[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; }
}

ご覧のとおり、yieldingメソッドのコンテキストからのすべてのローカル変数は、この生成されたクラスのフィールドに移動されます。興味深い方法はm_Finally、名前に次のようなものがあります。

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

ご覧のとおり、これらのメソッドは使い捨てオブジェクト(FileStreamおよびStreamReader)を破棄します。いつ呼ばれた?列挙の最後、または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;
    }
}

First()の実装を見ると、次のように表示されます。最初のアイテムを返した後Enumerableに呼び出されます。Dispose

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

したがってDispose、自動生成されたクラスが呼び出され、破棄が必要なすべてのローカル変数がメソッドの呼び出しによって破棄されm_Finallyます。

ところで(LINQでの使用法ではありません)foreachステートメントの実装を見ると、列挙子が列挙後に破棄されていることがわかります。したがって、生成されたクラスは、例外Disposeの場合でも呼び出されます。break

于 2012-06-01T12:08:02.997 に答える
17

はい、処分されました。

[編集] LINQ メソッドまたは foreach ループを使用している限り、破棄は自動的に処理されます。ただし、メソッドを手動で呼び出すことにした場合.Enumerator().MoveNext()は、自分で破棄を処理する必要があります。[/編集]

このコード:

class something : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing");
        Console.WriteLine(Environment.StackTrace);
    }
}
static IEnumerable<string> ie()
{
    using (new something())
    {
        Console.WriteLine("first");
        yield return "first";
        Console.WriteLine("second");
        yield return "second";
    }
}
static void Main(string[] args)
{
    Console.WriteLine("before");
    ie().First();
    Console.WriteLine("after");
}

版画:

before
first
Disposing
   at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
   at System.Environment.get_StackTrace()
   at TestApp.Program.something.Dispose() in C:\Users\Tim\Documents\Visual Studi
o 2010\Projects\TestApp\TestApp\Program.cs:line 198
   at TestApp.Program.<ie>d__0.<>m__Finally2() in C:\Users\Tim\Documents\Visual
Studio 2010\Projects\TestApp\TestApp\Program.cs:line 0
   at TestApp.Program.<ie>d__0.System.IDisposable.Dispose() in C:\Users\Tim\Docu
ments\Visual Studio 2010\Projects\TestApp\TestApp\Program.cs:line 0
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at TestApp.Program.Main(String[] args) in C:\Users\Tim\Documents\Visual Studi
o 2010\Projects\TestApp\TestApp\Program.cs:line 214
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args
)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySec
urity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
after
于 2012-06-01T11:54:14.687 に答える
1

これは一般的な LINQ の問題/質問であり、はい - LINQ は、実行されると、取得したすべての破棄可能な要素を破棄します。

于 2012-06-01T12:01:52.497 に答える