210

DataSetとDataTableはどちらもIDisposableを実装しているため、従来のベストプラクティスでは、Dispose()メソッドを呼び出す必要があります。

ただし、これまで読んだことから、DataSetとDataTableには実際には管理されていないリソースがないため、Dispose()は実際には多くのことを行いません。

さらに、using(DataSet myDataSet...)DataSetにはDataTablesのコレクションがあるため、使用することはできません。

したがって、安全のために、myDataSet.Tablesを反復処理し、各DataTablesを破棄してから、DataSetを破棄する必要があります。

それで、すべてのDataSetとDataTableでDispose()を呼び出すのは面倒な価値がありますか?

補遺:

DataSetを破棄する必要があるとお考えの方へ:一般に、破棄のパターンはusingorを使用するtry..finallyことです。これは、Dispose()が呼び出されることを保証するためです。

ただし、これはコレクションにとって醜いものになります。たとえば、Dispose()の呼び出しの1つが例外をスローした場合はどうしますか?次の要素を処分し続けることができるように、それを飲み込みますか(これは「悪い」です)?

または、myDataSet.Dispose()を呼び出すだけで、myDataSet.TablesのDataTablesを破棄することを忘れることをお勧めしますか?

4

12 に答える 12

158

ここでは、DataSetにDisposeが不要な理由を説明するいくつかの説明を示します。

処分するか処分しないか?

DataSetのDisposeメソッドは、継承の副作用のためにのみ存在します。つまり、実際には、ファイナライズに役立つことは何もしません。

DisposeはDataTableおよびDataSetオブジェクトで呼び出す必要がありますか?MVPからの説明が含まれています:

system.data名前空間(ADONET)には、管理されていないリソースは含まれていません。したがって、特別なものを追加しない限り、それらを破棄する必要はありません。

Disposeメソッドとデータセットを理解していますか?権威スコットアレンからのコメント付き:

実際には、ほとんどメリットがないため、DataSetを破棄することはめったにありません。」

したがって、現在、DataSetでDisposeを呼び出す正当な理由はないというコンセンサスがあります。

于 2009-05-26T23:29:07.663 に答える
136

更新(2009年12月1日):

この回答を修正し、元の回答に欠陥があることを認めたいと思います。

元の分析、ファイナライズが必要なオブジェクトに適用されます。正確で詳細な理解がなければ、実践を表面的に受け入れるべきではないという点は依然として存在します。

ただし、DataSets、DataViews、DataTablesは、コンストラクターでのファイナライズを抑制していることがわかりました。これが、それらに対してDispose()を明示的に呼び出しても何も起こらない理由です。

おそらく、これは管理されていないリソースがないために発生します。したがって、 MarshalByValueComponentが管理されていないリソースを考慮に入れているにもかかわらず、これらの特定の実装には必要がないため、ファイナライズを省略できます。

(.NETの作成者は、通常最も多くのメモリを占有するタイプのファイナライズを抑制するように注意します。これは、ファイナライズ可能なタイプの一般的なこの方法の重要性を示しています。)

それにもかかわらず、.NET Frameworkの開始(ほぼ8年前)以来、これらの詳細はまだ十分に文書化されていません(本質的に、競合するあいまいな資料をふるいにかけて断片をまとめるのは自分のデバイスに任されています)。時々イライラしますが、私たちが日常的に依存しているフレームワークのより完全な理解を提供します)。

たくさん読んだ後、ここに私の理解があります:

オブジェクトがファイナライズを必要とする場合、必要以上にメモリを占有する可能性があります。その理由は次のとおりです。a)デストラクタを定義する(またはデストラクタを定義するタイプから継承する)タイプは、ファイナライズ可能と見なされます。b)割り当て時に(コンストラクターが実行される前に)、ポインターがファイナライズキューに配置されます。c)ファイナライズ可能なオブジェクトでは、通常、(標準の1つではなく)2つのコレクションを再利用する必要があります。d)ファイナライズを抑制しても、ファイナライズキューからオブジェクトは削除されません(SOSの!FinalizeQueueによって報告されます)このコマンドは誤解を招く可能性があります。(それ自体で)ファイナライズキューにあるオブジェクトを知ることは役に立ちません。どのオブジェクトがファイナライズキューにあり、それでもファイナライズが必要かを知ることは役に立ちます(これに対するコマンドはありますか?)

ファイナライズを抑制すると、オブジェクトのヘッダーが少しオフになり、ファイナライザーを呼び出す必要がないことをランタイムに示します(FReachableキューを移動する必要はありません)。それはファイナライズキューに残ります(そしてSOSの!FinalizeQueueによって報告され続けます)

DataTable、DataSet、DataViewクラスはすべて、管理されていないリソースを(潜在的に)処理できるファイナライズ可能なオブジェクトであるMarshalByValueComponentをルートとしています。

  • DataTable、DataSet、DataViewはアンマネージリソースを導入しないため、コンストラクターでのファイナライズを抑制します
  • これは珍しいパターンですが、発信者は使用後にDisposeを呼び出すことを心配する必要がありません。
  • これと、DataTablesが異なるDataSet間で共有される可能性があるという事実は、DataSetsが子DataTablesを破棄することを気にしない理由である可能性があります
  • これは、これらのオブジェクトがSOSの!FinalizeQueueの下に表示されることも意味します
  • ただし、これらのオブジェクトは、ファイナライズできない対応物と同様に、単一のコレクションの後で再利用可能である必要があります。

4(新しい参照):

元の回答:

これには多くの誤解を招くような一般的に非常に貧弱な答えがあります-ここに着陸した人は誰でもノイズを無視し、以下の参考文献を注意深く読む必要があります。

間違いなく、DisposeはFinalizableオブジェクトで呼び出す必要があります。

DataTablesファイナライズ可能です。

Disposeを呼び出すと、メモリの再利用が大幅に高速化されます。

MarshalByValueComponentは、Dispose()でGC.SuppressFinalize(this)を呼び出します。これをスキップすると、メモリが再利用される前に、数百ではないにしても数十のGen0コレクションを待機する必要があります。

ファイナライズのこの基本的な理解により、私たちはすでにいくつかの非常に重要なことを推測することができます。

まず、ファイナライズが必要なオブジェクトは、そうでないオブジェクトよりも長持ちします。実際、彼らはずっと長生きすることができます。たとえば、gen2にあるオブジェクトをファイナライズする必要があるとします。ファイナライズはスケジュールされますが、オブジェクトはまだgen2にあるため、次のgen2コレクションが発生するまで再収集されません。実際、これは非常に長い時間になる可能性があります。実際、gen2コレクションはコストがかかるため、非常にまれにしか発生しないため、状況が順調に進んでいる場合は長い時間がかかります。ファイナライズが必要な古いオブジェクトは、スペースが再利用される前に、数百ではないにしても数十のgen0コレクションを待機する必要がある場合があります。

第二に、ファイナライズが必要なオブジェクトは巻き添え被害を引き起こします。内部オブジェクトポインタは有効なままである必要があるため、ファイナライズを直接必要とするオブジェクトがメモリに残るだけでなく、オブジェクトが直接および間接的に参照するすべてのものもメモリに残ります。オブジェクトの巨大なツリーが、ファイナライズを必要とする単一のオブジェクトによって固定されている場合、ツリー全体が残り、先ほど説明したように、潜在的に長い間残ります。したがって、ファイナライザーを慎重に使用し、内部オブジェクトポインターができるだけ少ないオブジェクトにファイナライザーを配置することが重要です。先ほど示したツリーの例では、ファイナライズが必要なリソースを別のオブジェクトに移動し、そのオブジェクトへの参照をツリーのルートに保持することで、問題を簡単に回避できます。

最後に、ファイナライズが必要なオブジェクトは、ファイナライザースレッドの作業を作成します。ファイナライズプロセスが複雑な場合、唯一のファイナライザースレッドがこれらのステップの実行に多くの時間を費やすため、作業のバックログが発生し、ファイナライズを待機するオブジェクトが増える可能性があります。したがって、ファイナライザーが行う作業をできるだけ少なくすることが非常に重要です。また、すべてのオブジェクトポインターはファイナライズ中も有効なままですが、それらのポインターが既にファイナライズされたオブジェクトにつながる場合があるため、役に立たない可能性があることにも注意してください。ポインタが有効であっても、ファイナライズコードでオブジェクトポインタをたどらないようにするのが一般的に最も安全です。安全で短いファイナライズコードパスが最適です。

Gen2で数百MBの参照されていないDataTableを目にした人から引用してください。これは非常に重要であり、このスレッドの回答では完全に見逃されています。

参照:

1 - http://msdn.microsoft.com/en-us/library/ms973837.aspx

2 - http: //vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx

3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

于 2009-10-21T20:47:36.420 に答える
27

現在の.NETFrameworkの化身では何も行わない場合でも、何か便利なことを実行し、Disposeを呼び出す必要があります。将来のバージョンでその状態が維持され、リソースの使用効率が低下するという保証はありません。

于 2009-05-26T23:25:02.960 に答える
18

オブジェクトに管理されていないリソースがない場合でも、破棄するとオブジェクトグラフが壊れてGCに役立つ場合があります。一般に、オブジェクトがIDisposableを実装している場合は、Dispose()を呼び出す必要があります。

Dispose()が実際に何かを実行するかどうかは、指定されたクラスによって異なります。DataSetの場合、Dispose()実装はMarshalByValueComponentから継承されます。コンテナから自分自身を削除し、Disposedイベントを呼び出します。ソースコードは以下のとおりです(.NET Reflectorで分解):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
于 2009-05-27T08:39:05.263 に答える
8

DataTablesを自分で作成しますか?すべての子メンバーを破棄するのは親の仕事であるため、(DataSet.Tablesのように)オブジェクトの子を反復処理する必要は通常ありません。

一般的に、ルールは次のとおりです。作成し、IDisposableを実装している場合は、破棄します。作成しなかった場合は、破棄しないでください。これが親オブジェクトの仕事です。ただし、各オブジェクトには特別なルールがある場合があります。ドキュメントを確認してください。

.NET 3.5の場合、「使用しなくなったら破棄する」と明示的に示されているので、それを実行します。

于 2009-05-27T08:49:48.627 に答える
7

オブジェクトがIDisposeableを実装するときはいつでもdisposeを呼び出します。それには理由があります。

データセットは、メモリを大量に消費する可能性があります。クリーンアップのマークを付けるのが早ければ早いほどよいでしょう。

アップデート

この質問に答えてから5年になります。私はまだ私の答えに同意します。disposeメソッドがある場合は、オブジェクトの処理が完了したときに呼び出す必要があります。IDisposeインターフェースが実装されたのには理由があります。

于 2009-05-26T23:19:21.177 に答える
5

この質問の意図またはコンテキストが本当にガベージコレクションである場合は、データセットとデータテーブルを明示的にnullに設定するか、キーワードus​​ingを使用してそれらをスコープ外にすることができます。テトラニュートロンが以前に言ったように、廃棄はあまり効果がありません。GCは、参照されなくなったデータセットオブジェクトと、スコープ外のデータセットオブジェクトを収集します。

私は本当に、SOが反対票を投じる前に、実際にコメントを書くことを強制したことを望みます。

于 2009-05-26T23:34:55.360 に答える
1

データセットは、IDisposableを実装するMarshalByValueComponentを介してIDisposableを実装します。データセットは管理されているため、disposeを呼び出すことによる実際のメリットはありません。

于 2009-05-26T23:18:25.017 に答える
0

Clear()関数を使用してみてください。それは私が処分するのに最適です。

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
于 2013-11-20T16:49:15.097 に答える
0

DataSetはMarshalByValueComponentクラスを継承し、MarshalByValueComponentはIDisposableインターフェイスを実装するため、Dispose()を実行する必要はありません。

于 2018-10-11T12:51:45.930 に答える
0

これは、を適切に破棄する正しい方法DataTableです。

private DataTable CreateSchema_Table()
{
    DataTable td = null;
    try
    {
        td = new DataTable();
        //use table DataTable here
        
        return td.Copy();
    }
    catch {  }
    finally
    {
        if (td != null)
        {
            td.Constraints.Clear();
            td.Clear();
            td.Dispose();
            td = null;
        }
    }
}
于 2020-09-30T17:31:29.263 に答える
0

そして、これは、によって消費されたメモリを破棄および解放するための最良/適切な方法DataSetです。

try
    {
        DataSet ds = new DataSet("DS");
        //use table DataTable here
        
    }
    catch {  }
    finally
    {
        if (ds != null)
                {
                    ds.EnforceConstraints = false;
                    ds.Relations.Clear();
                    int totalCount = ds.Tables.Count;

                    for (int i = totalCount - 1; i >= 0; i--)
                    {
                        DataTable td1 = ds.Tables[i];
                        if (td1 != null)
                        {
                            td1.Constraints.Clear();
                            td1.Clear();
                            td1.Dispose();
                            td1 = null;
                        }
                    }

                    ds.Tables.Clear();
                    ds.Dispose();
                    ds = null;
                }
    }
于 2020-09-30T17:40:25.693 に答える