3

たとえば、Sharepoint を使用していて (これは他のオブジェクト モデルにも当てはまります)、ステートメントの途中で、IDisposable SPWeb オブジェクトを作成するメソッド (この場合は "OpenWeb()") を呼び出します。ここで、SPWeb オブジェクトへの参照がないため、SPWeb オブジェクトで Dispose() を呼び出すことができません。 では、このメモリ リークについて心配する必要はありますか?

SPUser spUser = SPControl.GetContextSite(HttpContext.Current).OpenWeb().SiteUsers[@"foo\bar"];

ステートメントを複数の行に分割して、Dispose を呼び出すための SPWeb 参照を取得できることはわかっています。

SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb();
SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
spWeb.Dispose();

私の質問は美学に関するものではなく、参照がないため、明示的に Dispose() を呼び出すことができない IDisposable オブジェクトに何が起こるかについての質問であることに注意してください。

最初に質問した時、言葉足らずで申し訳ありませんでした。それ以来、私はそれを言い換えました。これまでのすべての応答に感謝します。

4

9 に答える 9

5

「明示的に Dispose() を呼び出すことができない IDisposable オブジェクトはどうなりますか?」

一般に、すべての破棄可能なオブジェクトで Dispose を (using ステートメントを使用して暗黙的に、または明示的に) 呼び出すことができますが、それができない仮想シナリオでは、オブジェクトの実装方法によって異なります。

一般に、.Net オブジェクトはこれらの線に沿ったパターンに従います。パターンは、dispose が呼び出されない場合にクリーンアップするファイナライザーを定義し、dispose でファイナライザーを抑制することです。これにより、メモリ負荷が軽減され、GC の作業が少なくなります。

ファイナライザーから Dispose を呼び出す際の多くの問題の 1 つは、シングル スレッドの問題をマルチスレッドの問題に変換しようとしているということです。また、管理されていないリソースを予想よりも長く保持することになります (たとえば、ファイルを開いて、close または dispose を呼び出すのを忘れ、次に開こうとするとロックされます)。

要するに、すべての破棄可能なオブジェクトを破棄するのがベスト プラクティスです。そうしないと、奇妙で​​複雑なバグが発生する可能性があります。sharepoint などの一部のフレームワークは、ドキュメントに従って破棄してはならないオブジェクトの共有インスタンスを返すことに注意してください。

通常、オブジェクトを「using」パターンで破棄すると、コードがはるかに読みやすくなります。Dispose を明示的に呼び出す (object.Dispose()) 場合の問題は、オブジェクトが割り当てられた場所を追跡するのが難しく、忘れやすいことです。using ステートメントの中括弧を閉じることを忘れないでください。コンパイラは文句を言います :)

編集 / 落とし穴

MS のドキュメントによると、GetContextSite によって返される共有ポイント共有オブジェクトへの参照に対して dispose を呼び出すべきではありません。したがって、ここでは特に注意する必要があります。

使用する必要がある安全な共有ポイント パターンについては、この回答を参照してください。

ただし、オブジェクトが Web パーツの GetContextSite メソッドによって提供される場合など、共有リソースへの参照がある場合は、どちらのメソッドも使用してオブジェクトを閉じないでください。共有リソースでいずれかの方法を使用すると、アクセス違反エラーが発生します。共有リソースへの参照があるシナリオでは、代わりに Windows SharePoint Services またはポータル アプリケーションでオブジェクトを管理します。

于 2009-01-06T04:03:44.393 に答える
4

以下は、より慣用的で読みやすくなっています。

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}
于 2009-01-06T03:13:17.383 に答える
3

Dispose メソッドを明示的に (またはusingステートメントを介して暗黙的に) 呼び出す必要があります。コードを複数行に分割するもう 1 つの理由は次のとおりです。

  • 可読性
  • デバッグしやすい

Dispose メソッドはファイナライザーで実行できますが、自分で呼び出した方が安全です。

于 2009-01-06T03:10:30.227 に答える
3

行を分割して Dispose を使用することをお勧めします。オブジェクトが IDisposable を実装している場合は、破棄が必要であると想定して、using ブロックで使用する必要があります。

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SpUser spUser = null;
    if (spWeb != null)
    {
        spUser = spWeb.SiteUsers[@"foo\bar"];
    }
}

これを行うことで、オブジェクトを破棄し、外部リソースを開く OpenWeb() 呼び出しでエラーを処理することもできます。

于 2009-01-06T03:27:09.010 に答える
1

これまでに言われてきたように、作成していないオブジェクトを決して破棄してはなりません。したがって、あなたの例では、SPWeb または SPSite オブジェクトを破棄しないでください。

これにより、現在の SPRequest オブジェクトが破棄されます。まだ機能しているように見えるかもしれませんが、たとえば後で新しい Web パーツを追加したり、Web パーツのツール ウィンドウを開こうとしたりすると、さまざまな奇妙なエラーが発生します。

すでに述べたように、自分で作成したSPWeb と SPSite のインスタンス(新規) を破棄する必要があります。

これは、using() または try/finally を使用して行うことができます (これは、とにかく MSIL コードに using() ステートメントが表示される方法です)。try/finally を使用する場合は、SPWeb/SPSite インスタンスで null を確認し、最初に SPWeb を確認することをお勧めします。これは、SPSite が SPWeb を自動的に破棄するためです。

覚えておくべきもう 1 つの重要な点は、AllWebs や Webs などの SPWebCollections をループする場合は、サブ Web をループ処理するときにサブ Web を破棄することです。多くのサブ Web があり、メモリの可能性が限られている 32 ビット ハードウェアで実行している場合、SPRequest オブジェクトで非常に高速にメモリをいっぱいにすることができます。これにより、アプリケーション プールが定期的にリサイクルされるため、パフォーマンスが低下します。

そうは言っても、コード例のように呼び出しを結合しないことも良い習慣です。読むのは難しいですし、破棄すべき SPWeb を使用していた場合、それはできません! この種のメモリ リークは見つけるのが最も難しいため、行わないでください ;-)

詳細については、 Roger Lamb のブログを 勧めします。 -spweb-and-spsite-objects.aspx

アンダース

于 2009-01-18T20:57:17.087 に答える
1

メモリリーク?いいえ、IDisposable の実装がクラス ライブラリのガイドラインに準拠していれば、次のガベージ コレクションでクリーンアップされるため、心配する必要はありません。

ただし、使用する IDisposable の実装の有効期間を適切に管理していないため、コードのエラーが明らかになります (この問題についてはhttp://www.caspershouse.com/post/A-で詳しく説明しています)。 Better-Implementation-Pattern-for-IDisposable.aspx )。2 番目のコード ブロックは最初のステップとしては適切ですが、SiteUsers の呼び出しが失敗した場合に Dispose の呼び出しが保証されるわけではありません。

修正したコードは次のようになります。

// Get the site.
var contextSite = SPControl.GetContextSite(HttpContext.Current);

// Work with the web.
using (SPWeb web = contextSite.OpenWeb())
{
  // Get the user and work with it.
  SPUser spUser = web.SiteUsers[@"foo\bar"];
}
于 2009-01-06T03:18:13.067 に答える
1

ここでの多くの回答は、最終的に Dispose が呼び出されることだけが重要であると想定しています。ただし、SPSite と SPWeb を使用する場合は、できるだけ早く Dispose() を呼び出したいと思うでしょう。いつ行うべきかを判断するのは難しい場合が多いですが、その質問に答えるのに役立つ参考文献がたくさんあります。

なぜそうなのかについて、Stefan Goßner がここで素晴らしい要約を提供しています。

各 SPWeb および SPSite オブジェクトは、バックエンド SQL サーバーとの通信を担当する SharePoint COM オブジェクトへの参照を保持する SPRequest オブジェクトへの参照を保持します。

SPWeb オブジェクトを破棄しても、実際には SPWeb オブジェクトがメモリから削除されるわけではありません (実際には、.NET フレームワークでは決定論的な方法でメモリからオブジェクトを削除することはできません)。ただし、COM オブジェクトを閉じる原因となる SPWeb オブジェクトのメソッドを呼び出します。 SQL サーバーへの接続を切断し、割り当てられたメモリを解放します。

つまり、バックエンド SQL サーバーへの接続は、SPRequest オブジェクトが作成された瞬間から SPWeb オブジェクトが破棄されるまで開いたままになります。

コード サンプルのベスト プラクティスは次のようになります。

SPSite contextSite = SPControl.GetContextSite(HttpContext.Current);
using (SPWeb spWeb = contextSite.OpenWeb())
{
  SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
  // Process spUser
}
// DO NOT use spUser
// DO NOT dispose site from HttpContext

親 SPWeb が破棄された後に、SPUser、SPList などの SP オブジェクトを使用するのは安全ではないことに注意してください。

于 2009-01-19T01:45:10.373 に答える
0

http://msdn.microsoft.com/en-us/library/aa973248.aspx ベスト プラクティスから: 使い捨て可能な Windows SharePoint Services オブジェクトの使用:

SPSite オブジェクトが SPControl.GetContextSite から取得された場合、呼び出し元のアプリケーションはオブジェクトを破棄してはなりません。SPWeb および SPSite オブジェクトは、この方法で派生した内部リストを保持しているため、オブジェクトを破棄すると、SharePoint オブジェクト モデルが予期しない動作をする可能性があります。

于 2009-01-18T13:06:07.603 に答える
-1

誰もまだこれを投入していないようです:

オブジェクトがディスポーザを必要とする (つまり、解放する必要のあるリソースを取得する) 場合は、ディスポーザのメソッドを呼び出すファイナライザを実装する必要があります。

ディスポーザーで、次の行を追加できます。

System.GC.SuppressFinalize(this)

ディスポーザーを呼び出すと、ファイナライズが妨げられます。そうすれば、必要に応じてオブジェクトを適切に使用できますが、ファイナライザーを介してオブジェクト自体をクリーンアップすることが保証されます (これが、C# にファイナライザーがある理由です)。

于 2009-01-22T05:20:06.163 に答える