C# одноразовые предметы

есть ли некоторые советы о том, как я должен иметь дело с IDisposable объект последовательности?

например, у меня есть метод, который строит IEnumerable<System.Drawing.Image> последовательности и в какой-то момент мне нужно будет удалить эти объекты вручную, потому что в противном случае это может привести к некоторым утечкам.

теперь, есть ли способ связать Dispose() вызовите действия сборщика мусора, потому что я хочу, чтобы эти объекты были удалены прямо в тот момент, когда они не работает из других части кода?

**или может вы мне посоветуете какой-то?**


как правило, это та же проблема, что и, например, в unmanaged C++ без общие указатели, где вы можете иметь метод:

SomeObject* AllocateAndConstruct();

и тогда вы не можете быть уверены, когда его утилизировать, если вы не используете контракты кода или не указываете что-то в комментариях.

Я думаю, что ситуация с одноразовыми объекты довольно одинаковые, но я надеюсь, что для этого есть подходящее решение.

7 ответов


вы должны спроектировать свою систему таким образом, чтобы вы знаю когда ресурсы больше не нужны. В худшем случае они будут в конечном итоге удалены, когда сборщик мусора доберется до него, но смысл IDisposable заключается в том, что вы можете освободить важные ресурсы раньше.

Это "ранее" зависит от вас, чтобы определить, например, вы можете освободить их, когда окно, которое их использует, закрывается или когда ваша единица работы завершает выполнение любых операций над ними. Но в какой-то момент какой-то объект должен "владеть" этими ресурсами и поэтому должен знать, когда они больше не нужны.


(от вопроса)

теперь, есть ли способ связать Dispose () вызов сборщика мусора действия, потому что я хочу эти объекты они расположены прямо в тот момент, когда они недоступно из другого кода запчасти?

GC не происходит тут когда ваш объект выходит за пределы области / досягаемости; он недетерминирован. К тому времени, когда GC видит это, нет смысла делать что-либо еще (это еще не обработано финализатор), так как уже слишком поздно.

фокус в том, чтобы знаю когда вы закончите с ним, и позвоните Dispose() себя. Во многих случаях using добивается этого. Например, вы можете написать класс, который реализует IDisposable и инкапсулирует набор Images-и оберните использование этого инкапсулирующего объекта using. The Dispose() на обертке может Dispose() все сохраненные изображения.

то есть

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

однако, это немного сложнее, если данные отображаются в пользовательском интерфейсе. Там нет ярлыка; вам придется отслеживать, когда вы закончите с каждым изображением, или принять недетерминированное завершение.


если вы хотите determiniscally уничтожать объекты в коллекции, вы должны позвонить 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#, если вы не утилизируете свой объект, сборщик мусора в конечном итоге запустится и очистит его.


вы можете использовать блок "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
    }
}

используя какой-то умный дизайн, процесс добавления объектов в контекст может стать еще проще. Можно дать контекст методу создания или опубликовать его через статический синглтон. Таким образом, он будет доступен и для областей дочерних методов-без необходимости передавать ссылку на contex вокруг. Используя эту схему, можно даже имитировать функцию искусственного деструктора, такую как f.e. известно из C++.


метод neat будет заключаться в создании собственного универсального класса коллекции, реализующего IDisposable. Когда этот класс коллекции Disposed () запрашивает каждый элемент, если он реализует IDisposed, и если да, то Dispose.

пример (посмотрите в другом месте, если вы не знаете о шаблоне 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 Кстати: если вы загрузили растровое изображение из файла, и вы забыли удалить () растровое изображение, которое вы не знаете, когда вы можете удалить этот файл.


метод neat будет заключаться в создании собственного универсального класса коллекции, реализующего IDisposable. Когда этот класс коллекции Disposed () запрашивает каждый элемент, если он реализует IDisposed, и если да, то Dispose.

пример (посмотрите в другом месте, если вы не знаете о шаблоне 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 Кстати: если вы загрузили растровое изображение из файла, и вы забыли удалить () растровое изображение, которое вы не знаете, когда вы можете удалить этот файл.


можно назвать GC.Collect() Если вы действительно должны были сразу же избавиться от этих объектов, но, насколько я понимаю, это зависит от GC, чтобы решить, собирать ли память.
Это в свою очередь вызовет Finalize() метод для каждого объекта, который должен быть выпущен.
Обратите внимание, что если коллекция выходит за рамки, GC в конечном итоге соберет память, используемую изображениями.
Можно также использовать конструкцию using, если используется коллекция, реализующая IDisposeable. Это гарантирует что объекты будут удалены точно, когда коллекция выходит за пределы области (или почти после окончания области).