【问题标题】:How to make an IDisposable object a class variable?如何使 IDisposable 对象成为类变量?
【发布时间】:2011-01-27 07:09:06
【问题描述】:

我正在使用 C# 处理 Active Directory。实例化PrincipalContext 对象似乎很昂贵,所以我想将一个存储在类变量中。

当使用PrincipalContext 作为局部变量时,我可以使用方便的using 语法。将IDisposable 对象存储在静态变量中时,如何确保正确处置该对象?

【问题讨论】:

  • 您希望何时处理?
  • 好问题! - 我在想当类型被垃圾收集时。大概当 GC 检测到内存中的对象没有对该类型的实例留下任何引用时会发生这种情况?
  • @Ben:不要依赖 GC 来处理你的对象。任何实现 IDisposable 的对象都必须被确定性地处理掉,否则它不会实现接口。 GC 收集是动态的,甚至永远不会保证发生。
  • 有些 CLR 类型永远不会被 GC 收集。如果您不Dispose 它们,它们将一直存在于您的应用程序中,直到关闭,无论对象是否引用它们。
  • @kyoryu - 出于兴趣,你能给我一个这样的 CLR 类型的例子吗?

标签: c# active-directory idisposable class-variables


【解决方案1】:

一般模式是在你的类上实现IDisposable 接口。举个例子:

public class YourClass : IDisposable
{
    private OtherDisposableType yourResource;

    public YourClass()
    {
        yourResource = new OtherDisposableType();
    }

    public void Dispose()
    {
        yourResource.Dispose();
    }
}

这至少是您需要做的。

编辑

我之前的版本提倡在所有情况下都遵循终结器模式,这被(正确地)指出违反了框架设计指南。但是,如果您实际上是在处理非托管资源(例如,您正在进行直接 P/Invoke 调用并获得需要显式释放的句柄),建议您创建一个终结器并调用 Dispose在其中防止有人使用您的代码并且不调用Dispose

public class YourClass : IDisposable
{
    private OtherDisposableType yourResource;

    public YourClass()
    {
        yourResource = new OtherDisposableType();
    }

    public void Dispose()
    {
        yourResource.Dispose();

        GC.SuppressFinalize(this);
    }

    ~YourClass()
    {
        Dispose();
    }
}

【讨论】:

  • 非常感谢,但是当实例符合 GC 条件时不会调用析构函数,并且我希望我的静态变量在实例之间保持不变?
  • @Ben:我不推荐静态变量。如果你强烈地觉得你需要走那条路,那么唯一的方法是在你的类上添加一个静态的Dispose 方法,当你的应用程序关闭时(或在任何适当的时间可能)显式调用该方法。
  • 一开始还不错。但是在建议实施终结器后走下坡路。删除它,你就会发布一个很好的答案。
  • 实际上,Microsoft 建议不要创建终结器,除非您正在处理非托管资源。即使这样,您通常也可以通过使用像 SafeHandle 这样的包装器来避免它。有关详细信息,请参阅《框架设计指南》一书中有关 Dispose 模式的部分。 amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/…
  • 你不应该从终结器中调用其他对象——它们可能已经自己完成了!如果对象包装了一些必须绝对释放的非托管资源,那么它已经有自己的终结器。
【解决方案2】:

看看System.ComponentModel 命名空间做了什么。基本上,我通常使用的模式是拥有一组子组件,其中包括我拥有的所有不是“价值”的东西——无论它是否实现IDisposable

然后,当我自己Dispose() 时,我会遍历这个集合和Dispose 任何实现IDisposable 的东西。

这种技术的一个优点是,如果我拥有的对象一开始不是 Disposable,但后来添加了 IDisposable 接口,我的类将做正确的事情而无需更改。

此外,使用 DI/IoC 容器可以为您处理大部分此类问题。

【讨论】:

  • 不是我所说的——不一定是一次性集合,而是对象的任何子组件都存储在集合中(任何类型)。一次性收藏的想法对我来说听起来有点吓人,尽管我目前还不太清楚为什么。
【解决方案3】:

所以基本上你想缓存一个昂贵的资源。这是一件好事。

全局数据(在这种情况下为静态变量)并不是一件好事,恕我直言。相反,为什么不让它成为一个实例变量并控制生命周期呢?

编写处理 AD 职责的类,让它创建和使用 PrincipalContext,并将其设为 IDisposable(使用 Dispose Pattern)。从中提取一个接口以将其解耦并使使用它的类更易于测试。

想要使用 AD 服务的类将采用新接口的构造函数参数(Dependency Injection 或 DI)。您可以在 using 块中手动创建类并将其传递给类或使用 DI Container Framework。您可以让框架将 AD 对象的生命周期设置为容器的生命周期(也可以是 IDisposable)。有关更多信息,请参阅How do you reconcile IDisposable and IoC? 和您的 DI 容器文档。

【讨论】:

  • 我真的很喜欢这个答案,谢谢。所产生的讨论在我的脑海中留下了一个学术问题,即如何最好地处理存储在类变量中的 IDisposable 类型(但这可能是由于我的理解不足)。
  • @Ben - 谢谢!我同意我们没有回答这个问题(尽管 Adam Robinson 对静态 Dispose 方法的评论值得考虑)。我倾向于将静态/类变量(字符串和正则表达式等只读不可变变量除外)视为代码异味,类似于其他语言中的全局变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
  • 2014-10-07
相关资源
最近更新 更多