C#一次性物品

时间:2011-04-26 09:57:11

标签: c# collections dispose disposable

是否有关于如何处理IDisposable对象序列的建议?

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

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

** 或者你可以建议我采取其他方法吗? **


通常,这似乎是一个问题,例如,在没有共享指针的非托管C++中,您可以使用方法:

SomeObject* AllocateAndConstruct();

然后你不能确定何时处理它,如果你不使用代码合同或者没有在评论中陈述某些内容。

我猜一次性物品的情况大致相同,但我希望有一个合适的解决方案。

7 个答案:

答案 0 :(得分:6)

(来自问题)

  

现在,有没有办法绑定   Dispose()调用垃圾收集器   动作,因为我想要这些对象   在它们出现的那一刻就处理好了   不再可以从其他代码访问   份?

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

然后,诀窍是知道完成后,然后自己致电Dispose()。在许多情况下using实现了这一目标。例如,您可以编写一个实现IDisposable的类并封装一组Image - 并使用using包装您对该封装对象的使用。包装器上的Dispose()可以Dispose()保存所有图像。

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

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

答案 1 :(得分: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#中,如果你不处理你的对象,垃圾收集器最终会运行并清理它。

答案 2 :(得分:5)

在不再需要资源时,您应该以知道的方式设计系统。在最糟糕的情况下,它们最终会在垃圾收集器到达时被处理掉,但IDisposable的意思是你可以提前释放重要的资源。

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

答案 3 :(得分:1)

您可以使用'using'块来确保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
    }
}

通过使用一些聪明的设计,将对象添加到上下文的过程可能会变得更加容易。可以将上下文赋予创建方法或通过静态单例发布它。这样,它也可用于子方法范围 - 无需传递对周围环境的引用。通过利用该方案,甚至可以模拟像f.e.这样的人工析构函数。从C ++中知道。

答案 4 :(得分:0)

整洁的方法是创建自己的实现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;
            }
            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中的所有bitMaps 顺便说一句:如果您从文件中加载了位图,并且忘记了Dispose()您不知道何时可以删除该文件的位图。

答案 5 :(得分:0)

整洁的方法是创建自己的实现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中的所有bitMaps 顺便说一句:如果您从文件中加载了位图,而忘记了Dispose()位图,则您不知道何时可以删除该文件。

答案 6 :(得分:-2)

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