閉じると破棄-どちらを呼び出すか?


161

スレッドを読んだことSqlCommand.Disposeで十分ですか?そして閉会と廃棄WCFサービスであれば、私は近くのDisposeむしろ閉じるよりも、私は、このようなSqlConnectionオブジェクトまたはStreamクラスから継承するいくつかのクラスの一つとしてクラスについて疑問に思って、それは問題ではありませんか?

回答:


190

この状況を明らかにしたいと思います。

MicrosoftのガイドラインによるとClose、適切な場所にメソッドを提供することは良い習慣です。ここからの引用であるフレームワークの設計ガイドラインは、

その地域の標準的な用語が近い場合はClose()、に加えてメソッドを提供することを検討してくださいDispose()。その際、Close実装を同じにすることが重要Disposeです...

ほとんどの場合Close、Disposeメソッドとメソッドは同等です。主な違いの間CloseとDisposeする場合にはSqlConnectionObject次のとおりです。

アプリケーションはClose複数回呼び出すことができます。例外は生成されません。

Disposeメソッド を呼び出すと、SqlConnectionオブジェクトの状態がリセットされます。破棄されたSqlConnection オブジェクトでメソッドを呼び出そうとすると、例外が発生します。

それは言った:

  • 接続オブジェクトを1回使用する場合は、を使用しますDispose。
  • 接続オブジェクトを再利用する必要がある場合は、Closeメソッドを使用します。

5
@ Chris、Close()のドキュメントは、「接続プールへの接続を解放するか、接続プールが無効になっている場合は接続を閉じます」と述べています。したがって、接続プールがオーバーフローしないようにするには、Close()で十分です。
— David Hammond

@DavidHammond:その通りです。以前のコメントを削除します。
— NotMe

3
.Dispose()も接続を解放してプールに戻しますか?
— oscilatingcretin

これは、このテーマについて10年間で私が読んだ最良の議論です。優れた点。
— マイケルエリクソン2017

1
したがって、このように機能します。1. con.Open() con.Close(); 2 con.Open(); // reuse 3. con.Dispose(); // use one time con.Open(); // error
— shaijut

24

いつものように、答えは次のとおりです。異なるクラスIDisposableは異なる方法で実装され、必要な研究を行うのはあなた次第です。

限りでSqlClientは、次のことをお勧めします。

using (SqlConnection conn = /* Create new instance using your favorite method */)
{
    conn.Open();
    using (SqlCommand command = /* Create new instance using your favorite method */)
    {
        // Do work
    }
    conn.Close(); // Optional
}

あなたは必要があります呼び出すことDispose(またはClose接続に*)!んではない、これは(少なくとも)次のGCサイクルまでプール内の接続をタイアップされます、あなたの接続をクリーンアップするガベージコレクタを待ちます。を呼び出す場合、を呼び出すDispose必要はありませんClose。また、using構成によってDispose正しく処理するのが非常に簡単になるため、を呼び出す理由はまったくありませんClose。

接続は自動的にプールされ、接続でDispose/ を呼び出しCloseても、接続は物理的には閉じられません(通常の状況)。独自のプーリングを実装しないでください。 SqlClient(データベースコンテキストと接続オプションの復元のように)接続がプールから取得されたときに、接続に対してクリーンアップを実行します。

*を呼び出す場合Closeは、例外に対して安全な方法(つまり、catchまたはfinallyブロック)で実行してください。


「必要な研究をするのはあなた次第」と言ったとき、その研究は何ですか?私が確かに言う方法を知っている唯一の方法はリフレクションを通してですが、それはほとんどの状況で「違法」であるという欠点があります。
— 嵐

6
私は言うことはありません:conn.Close(); // Optionalそれはオプションではありません。冗長で不要です。オブジェクトを2回破棄すると、一部のコード分析ツールによって警告としてマークされます。
— メタロジック2017

@Metalogic私は、適切な使用法でCloseを呼び出すことは不必要な(そして醜い)冗長であることに同意します。ただし、細心の注意:Closeの呼び出しは「破棄」ではありません(DisposeはSqlConnectionのCloseを意味します)。と比較してください。using (var x = ..) { x.Dispose(); }この場合、x実際には「2回破棄」されます。
— user2864740 2018年

11

Dispose()を呼び出す必要があります!

Dispose()は開発者が呼び出すためのもので、ガベージコレクターはFinalize()を呼び出します。オブジェクトでDispose()を呼び出さない場合、ガベージコレクターが近づき、finalizeを呼び出すまで、オブジェクトで使用されていたアンマネージリソースは破棄されません(誰がいつ発生するかがわかります)。

このシナリオは非確定的ファイナライズと呼ばれ、.net開発者にとって一般的な罠です。IDisposableを実装するオブジェクトを使用している場合は、それらに対してDispose()を呼び出します。

http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last

いくつかのオブジェクトでDisponse()を呼び出し、その接続でClose()を呼び出すか、ファイルハンドルを閉じるだけの多くのインスタンス(SqlConnectionなど)があるかもしれませんが、ほとんどの場合、Dispose()を呼び出すのが最善です。非常に近い将来にオブジェクトを再利用する予定がない限り。


26
このコメントは完全に偽です。ガベージコレクターがを呼び出すことはありませんDispose。
— スティーブンクリアリー

3
結果:を実装するクラスで使用していない場合は、呼び出す必要があります。呼び出されるクラスがIDisposableを実装していて、その使用法を内のページにラップしている場合は、(意図されたしゃれなので、撃ってください)で破棄できます。ただし、AFAIK を明示的に利用するものでは、の使用をお勧めします。Dispose() using()IDisposableusing()Dispose()Close()Open()
— ルネKåbis

他のDBMSについてはわかりませんが、PostgreSqlでは両方を行うことはできません。あなたがいったんClose接続、Postgresは自動的に接続識別子を設定しますnull。それ以降、Disposeすでにに設定されているSQL接続識別子は使用できませんnull。
— ssd

10

のためSqlConnection、接続自体の観点からは、これらは同等です。Reflectorによると、ほとんどのメンバーをnullに設定することにより、Dispose()呼び出しClose()と、いくつかの追加のメモリ解放操作を実行します。

ストリームの場合、それらは実際には同等です。 Stream.Dispose()単にClose()を呼び出します。


1
本気ですか?MSDNは、それを継承したComponentものであり、呼び出しを試みても何もしないようですClose()。私は、内のどこにでも見ることができないDBConnectionか、SqlConnectionこれらの通知のいずれかにネクタイという。ただし、DisposeMe()どこにも参照されないプライベートがあります。
— Deanna

@Deannaそれがここでオーバーライドされます。github.com/dotnet/corefx/blob/...
— デヴィッドCumps

@DavidCumps私がそのコメントを書いてから4年間で変更されたようです。リンクが無効になりました。
— Deanna


6

この迅速なアドバイスは長い答えになりました。ごめんなさい。

タイラーが彼の素晴らしい答えで指摘したように、電話Dispose()は素晴らしいプログラミングの実践です。これは、このメソッドは必要なすべてのリソース解放を「まとめて」行うことになっているため、不要なオープンリソースがないためです。たとえば、テキストをファイルに書き込んで、ファイルを閉じる(リソースを解放する)のに失敗した場合、そのファイルは開いたままになり、GCが出て、必要な処理を行うまで、他のユーザーはそのファイルに書き込むことができません。完了しました。

さて、場合によってはStreamWriter.Close()、をオーバーライドするような、処理しているクラスに固有の「ファイナライズ」メソッドがあるでしょうTextWriter.Close()。実際、これらは通常、状況により適しています。Close()たとえば、StreamWriter Dispose()は、オブジェクトを処理する前にストリームと基になるエンコーダーをフラッシュします。涼しい!

ただし、MSDNを参照すると、Microsoftでさえ、多くのクローザーとディスポーザーに混乱していることがあります。このウェブページでは、たとえば、いくつかの例でClose()は暗黙の前に呼び出されDispose()(暗黙の理由がわからない場合は、usingステートメントを参照してください)、特に、それらは気にしません。なぜでしょうか?私も困惑しました。

私が考えた理由(そして、私は強調しますが、これは元の研究であり、私が間違っていると確かに評判を失う可能性があります)はClose()失敗する可能性があり、リソースを開いたままにすると例外が発生し、リソースDispose()は確実に解放されます。これがが常にコールを保護するDispose()必要Close()がある理由です(しゃれのために申し訳ありません)。

MyResource r = new MyResource();

try {
  r.Write(new Whatever());

  r.Close()
finally {
  r.Dispose();
}

そして、はい、Microsoftはその1つの例で失敗したと思います。おそらく、そのタイムスタンプはファイルにフラッシュされません。

明日、古いコードを修正します。

編集:申し訳ありません、ブラノン、私はあなたの答えにコメントすることはできませんがClose()、finallyブロックを呼び出すことは良い考えですか?それからの例外は、ブロックの残りを台無しにするかもしれないと思います、それはおそらく重要なクリーンアップコードを含むでしょう。

Brannon'sへの返信:すばらしい。Close()本当に必要なときに呼び出すことを忘れないでください(たとえば、ストリームを処理するとき-.NETでのSQL接続についてはあまり知らない)。


実際には、Close()を呼び出すことはありません。Dispose()と 'using'構文に適切な処理をさせるだけです。Disposeを呼び出さない場合は、例外に対して安全な方法でCloseを呼び出す必要があります。finallyブロックに例外処理を追加することは良い考えかもしれません。
— ブラノン

そう、私のコメントは特にSqlClientに関するものでした。ポイントは、使用しているクラスを理解する必要があるということです。常にDisposeを呼び出すことが必ずしも正しい答えであるとは限りません。
— ブラノン

2

iDisposableに型キャストし、その上でdisposeを呼び出します。これにより、関数の名前に関係なく、「iDisposable.Dispose」を実装するように構成されているメソッドが呼び出されます。


「関数の名前」「Dispose」:最初の質問に戻ります:}
— user2864740

関数はにバインドされていますがIDisposable.Dispose、それがその名前であるとは限りません。vb.netでは、関数をその関数の名前と関連付ける必要のない名前を持つ複数のインターフェイスメンバーにバインドすることが可能であることに注意してください。
— スーパーキャット2018年

:このようなキャストusing (myObj as IDisposable)
— Yousha Aleayoub

2

一般に、Close()、Abort()、Dispose()の問題に直面していますが、それらの違いについて説明します。

1)中止:-中止が呼び出されると、クライアントはサーバーに通知せずに接続を削除し、サーバーが一定時間(約1分)待機するため、これを使用することはお勧めしません。バルクリクエストがある場合、限られた接続プールでタイムアウトが発生する可能性があるため、abort()を使用できません。

2)閉じる:-接続を閉じるときにサーバーを呼び出し、サーバーがその側で閉じることを確認するため、接続を閉じるには非常に良い方法です。

ここで、もう1つ見てみましょう。場合によっては、エラーが発生したときに、そのconnection.close()にコードを書き込むのは適切な方法ではありません。その時点で通信状態に障害が発生するためです。

3)破棄:-これは一種のクローズですが、接続を閉じた後は、再度開くことはできません。

このようにしてみてください、

private void CloseConnection(Client client)
    {
        if (client != null && client.State == CommunicationState.Opened)
        {
            client.Close();
        }
        else
        {
            client.Abort();
        }
    }

チェックオンclient != nullは、すべての使用を保護するわけではないため、誤り/誤解を招くものです。また、コードがどのようにして「この接続は開かれておらず、閉じる必要がある」という状態になるのかわかりません。
— user2864740 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.