7

关于我应该如何处理IDisposable对象序列有一些建议吗?

例如,我有一个构建IEnumerable<System.Drawing.Image>序列的方法,在某些时候我需要手动处理这些对象,否则可能会导致一些泄漏。

现在,有没有办法将Dispose()调用绑定到垃圾收集器操作,因为我希望这些对象在它们不再可以从其他代码部分访问的那一刻被立即处理?

**或者你可以建议我一些其他的方法?**


通常,这似乎与它出现的问题相同,例如,在 unmanaged C++without shared pointers中,您可以有一个方法:

SomeObject* AllocateAndConstruct();

如果您不使用代码合同或未在评论中说明某些内容,那么您无法确定何时处置它。

我想一次性物品的情况也差不多,但我希望有一个合适的解决方案。

4

6 回答 6

7

(来自问题)

现在,有没有办法将 Dispose() 调用绑定到垃圾收集器操作,因为我希望在其他代码部分不再可访问这些对象时立即处理这些对象?

当您的对象超出范围/范围时,GC 不会立即发生;它是不确定的。当 GC 看到它时,做任何其他事情(终结器尚未处理)已经没有意义了,因为为时已晚。

那么,诀窍就是知道你什么时候完成它,然后打电话给Dispose()自己。在许多情况下using实现了这一点。例如,您可以编写一个实现IDisposable和封装一组Images 的类,并将您对该封装对象的使用用using. Dispose()包装纸上可以保存Dispose()所有图像。

IE

using(var imageWrapper = GetImages()) {
    foreach(var image in imageWrapper) {
         ...
    }
    // etc
} // assume imageWrapper is something you write, which disposes the child items

但是,如果您在 UI 上显示数据,这会有点棘手。那里没有捷径;您必须跟踪完成每个图像的时间,或者接受不确定的最终确定。

于 2011-04-26T10:02:09.327 回答
6

如果您想确定地处置集合中的对象,您应该调用Dispose每个:

myImages.ToList().ForEach(image => image.Dispose());

如果你不这样做,并且如果你的对象变得不可访问,GC 最终将运行并释放它们。

现在,如果您不想手动编写Dispose调用代码,您可以创建一个包装类来实现IDisposable并通过using语句使用它:

using (myImages.AsDisposable()) { 
  // ... process the images
}

这是所需的“基础设施”:

public class DisposableCollectionWrapper<D> : IDisposable
where D : IDisposable {

  private readonly IEnumerable<D> _disposables;

  public DisposableCollectionWrapper(IEnumerable<D> disposables) {
    _disposables = disposables;
  }

  public void Dispose() {
    if (_disposables == null) return;
    foreach (var disposable in _disposables) {
      disposable.Dispose();
    }
  }

}

public static class CollectionExtensions {

  public static IDisposable AsDisposable<D>(this IEnumerable<D> self)
  where D : IDisposable {
    return new DisposableCollectionWrapper<D>(self);
  }

}

另请注意,这您使用 C++ 描述的情况不同。在 C++ 中,如果你没有delete你的对象,你就会有真正的内存泄漏。在 C# 中,如果您不处置对象,垃圾收集器最终会运行并清理它。

于 2011-04-26T10:02:53.513 回答
5

您应该以您知道何时不再需要资源的方式设计您的系统。在最坏的情况下,它们最终会在垃圾收集器到达时被丢弃,但 IDisposable 的重点是您可以更早地释放重要资源。

这个“更早”由您来定义,例如,您可以在使用它们的窗口关闭时释放它们,或者当您的工作单元完成对它们执行的任何操作时。但是在某些时候,某个对象应该“拥有”这些资源,因此应该知道何时不再需要它们。

于 2011-04-26T10:02:55.563 回答
1

您可以使用“使用”块,以确保 IDisposable 在块离开后立即被释放。编译器确实将这些块封装到 try - finally 语句中,以确保在离开块时无论如何都会调用 Dispose。

通过使用终结器,可以使 GC 为那些以某种方式“错过”的对象调用 Dispose 方法。但是,实现终结器的成本更高,并且会降低垃圾收集效率 - 可能还会降低应用程序的整体性能。因此,如果可能的话,您应该尽量确保自己处置您的 IDisposables;确定性地:

public class Context : IDisposable {

    List<IDisposable> m_disposable = new List<IDisposable>();
    public void AddDisposable(IDisposable disposable) {
        m_disposable.Add(disposable); 
    }

    public void Dispose() {
        foreach (IDisposable disp in m_disposable)
            disp.Dispose(); 
    }

    // the Context class is used that way: 
    static void Main(string[] args) {

        using (Context context = new Context()) {
            // create your images here, add each to the context
            context.AddDisposable(image); 
            // add more objects here 

        } // <- leaving the scope will dispose the context
    }
}

通过使用一些巧妙的设计,将对象添加到上下文的过程可能会变得更加容易。可以为创建方法提供上下文或通过静态单例发布它。这样,它也可用于子方法范围 - 无需传递对上下文的引用。通过使用该方案,甚至可以模拟人工析构函数功能,例如 C++ 中已知的 fe。

于 2011-04-26T10:19:30.943 回答
1

简洁的方法是创建您自己的实现 IDisposable 的通用集合类。当这个集合类被 Disposed() 时,询问每个元素是否实现了 IDisposed,如果是,则将其丢弃。

示例(如果您不了解 IDisposable 模式,请查看其他地方)

public class MyDisposableList<T> : List<T> : IDisposable
{
    private bool disposed = false;

    ~MyDisposableList()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected void Dispose(bool disposing)
    {
        if (!disposed)
        {
            foreach (T myT in this)
            {
                IDisposable myDisposableT = myT as IDisposable;
                if (myDisposableT != null)
                {
                    myDisposableT.Dispose();
                }
                myT = null;
            }
            this.Clear();
            this.TrimExcess();
            disposed = true;
        }
    }
    ...
}

用法:

using (MyDisposableList<System.Drawing.Bitmap> myList = new ...)
{
    // add bitmaps to the list (bitmaps are IDisposable)
    // use the elements in the list
}

using 语句的末尾会自动处理 myList,从而处理 myList 中的所有位图顺便说一句:如果您从文件中加载位图并且忘记了 Dispose() 位图,则您不知道何时可以删除该文件。

于 2014-05-05T10:15:11.757 回答
-2

如果您真的要立即处理这些对象,您可以调用GC.Collect(),但据我了解,由 GC 决定是否收集内存。
这反过来将调用Finalize()每个应该释放的对象的方法。
请注意,如果集合超出范围,GC 最终将收集图像使用的内存。
如果您使用实现 IDisposeable 的集合,也可以使用 using 构造。这将保证当集合超出范围时(或几乎在范围结束之后),对象将被准确地释放。

于 2011-04-26T10:00:27.010 回答