在我无法显式调用 Dispose() 的语句中间创建的 IDisposable 对象会发生什么情况?

StackOverflow https://stackoverflow.com/questions/415313

假设我正在使用 Sharepoint(这也适用于其他对象模型),在语句中间,我调用一个方法,在本例中为“OpenWeb()”,该方法创建一个 IDisposable SPWeb 对象。现在,我无法在 SPWeb 对象上调用 Dispose(),因为我没有对它的引用。 那么我需要担心内存泄漏吗?

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

我知道我可以将该语句分成多行并获取 SPWeb 引用来调用 Dispose:

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

请记住,我的问题不是关于美观,而是更多关于我无法显式调用 Dispose() 的 IDisposable 对象会发生什么,因为我没有引用。

抱歉,我第一次问问题时不够清楚。从那以后我重新表述了它。感谢迄今为止所有的回复。

有帮助吗?

解决方案

“我无法明确调用Dispose()的IDisposable对象会发生什么?”

通常,您可以在所有一次性对象上调用Dispose(隐式使用using语句或显式),但在不能的假设情况下,取决于在实现对象的方式上。

通常,.Net对象将遵循这些行的模式。该模式是定义一个终结器,在没有调用dispose的情况下清除东西,然后使用dispose来抑制终结器。这减少了内存负载,并减少了GC的工作量。

在终结器中调用Dispose的许多问题中,你是将单线程问题转变为多线程问题,终结器将在不同的线程上运行,这可能会暴露一些非常微妙的问题难以捉住虫子。此外,这意味着您将持续超过预期的非托管资源(例如,您打开文件,忘记致电关闭或处置,下次再打开它锁定)

最重要的是,处理所有一次性对象是最佳实践,否则您可能会引入奇怪和复杂的错误。需要注意的是,某些框架(如sharepoint)会返回不应根据文档处理的对象的共享实例。

当我使用“using”对象处理我的对象时,我通常会发现代码更具可读性。图案。显式调用Dispose(object.Dispose())的问题在于,很难跟踪对象的分配位置,并且很容易忘记。你不能忘记关闭using语句的大括号,编译器会抱怨:)

编辑/ GOTCHA

根据 MS文档您不应该对GetContextSite返回的共享点共享对象的引用调用dispose。所以,你应该格外小心。

参见这个答案,您应该使用安全的共享点模式。

  

但是,如果你有一个参考   共享资源,如何时   对象由提供   Web部件中的GetContextSite方法,   不要使用任何一种方法来关闭   宾语。使用任何一种方法   共享资源导致Access   发生违规错误。在场景中   你有共享的引用   资源,而不是让Windows   SharePoint Services或您的门户   应用程序管理对象。

其他提示

以下内容更具惯用性,读起来也更好:

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

你应该显式地(或隐式地通过 使用 语句)调用Dispose方法。将代码拆分为多行的另一个原因是:

  • 可读性
  • 调试起来更容易

Dispose方法可以在终结器中执行,但自己调用更安全。

我建议拆分线并使用Dispose。如果一个对象实现了IDisposable,你必须假设它需要处理,因此在一个使用块中使用它。

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

通过执行此操作,您可以使用Dispose对象并处理打开外部资源的OpenWeb()调用中的错误。

记忆力泄漏?不,你不应该担心它,假设IDisposable的实现符合类库指南,因为下一个垃圾收集应该清理它。

但是,它确实在您的代码中显示错误,因为您没有正确管理您使用的IDisposable实现的生命周期(我在 http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable的.aspx )。您的第二个代码块是一个很好的第一步,但如果对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"];
}

正如迄今为止一些人所说:您绝不能处置不是您创建的对象。所以在你的例子中,你应该 不是 处理 SPWeb 或 SPSite 对象!

这将破坏当前的 SPRequest 对象。它可能看起来仍在工作,但是如果您稍后添加新的 Web 部件,或者尝试打开 Web 部件的工具窗格,您将收到各种奇怪的错误。

正如已经说过的,必须处理 SPWeb 和 SPSite 的实例 你自己创造的 (新的)。

这可以通过 using() 或 try/finally 来完成(这也是 using() 语句在 MSIL 代码中显示的方式)。如果您使用 try/finally,最佳做法是在 SPWeb/SPSite 实例上检查 null,并首先检查 SPWeb,因为 SPSite 会自动处置您的 SPWeb。

另一件需要记住的重要事情是,当循环像 AllWebs 或 Webs 这样的 SPWebCollections 时,要在循环访问子网站时处置它们。如果有很多子网站,并且您在内存潜力有限的 32 位硬件上运行,那么您确实可以用 SPRequest 对象非常快地填满内存。这将导致性能下降,因为它会导致您的应用程序池定期回收。

话虽如此,不要像代码示例中那样组合调用也是一个好习惯。很难阅读,并且 如果 你正在使用一个你应该处置的 SPWeb,但你不能!这种内存泄漏是最难发现的,所以不要这样做;-)

我可以推荐 Roger Lamb 的博客了解详细信息:http://blogs.msdn.com/rogerlaStefan Goßner 博客上还有一些技术细节:http://blogs.technet.com/stefan_gossner/archive/2008/12/05/dispose-spweb-and-spsite-objects.aspx

HTH系列 安德斯

这里的许多答案都假设最重要的是只调用Dispose。但是,在使用SPSite和SPWeb时,您肯定希望尽快调用Dispose()。确定何时应该通常很棘手,但有很多良好的参考资料可以帮助回答这个问题。

至于为什么会出现这种情况,Stefan Goß ner提供了一个很好的总结这里

  

每个SPWeb和SPSite对象都有一个   引用SPRequest对象   包含对SharePoint COM的引用   负责的对象   与后端SQL通信   服务器

     

处理SPWeb对象不会   实际上从中删除SPWeb对象   内存(实际上是.NET框架   不允许删除任何对象   以确定的方式从记忆中)   但它会调用SPWeb的方法   导致COM对象的对象   关闭与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对象是不安全的。

来自 http://msdn.microsoft.com/en-us/ library / aa973248.aspx 最佳实践:使用一次性Windows SharePoint Services对象

如果从SPControl.GetContextSite获取SPSite对象,则调用应用程序不应丢弃该对象。由于SPWeb和SPSite对象保留以这种方式派生的内部列表,因此处置该对象可能会导致SharePoint对象模型无法预测。

似乎没有人抛出这个:

如果你的对象需要一个处理器(即占用必须释放的资源),那么你应该实现一个调用处理器方法的终结器。

在处理器中,您可以添加以下行:

System.GC.SuppressFinalize(this)

如果您致电处理器,将阻止最终确定。这样你就可以在必要时很好地使用你的对象,但保证它通过终结器自行清理(这是C#确实有终结器的全部原因)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top