Пожизненная проблема IDisposable неуправляемых ресурсов в графе сложного объекта? - PullRequest
1 голос
/ 30 апреля 2010

Этот вопрос касается работы с неуправляемыми ресурсами (COM-взаимодействия) и обеспечения отсутствия утечек ресурсов. Буду признателен за отзыв о том, правильно ли я поступаю.

Справочная информация:


Допустим, у меня есть два класса:

  • Класс LimitedComResource, который является оберткой вокруг COM-объекта (получен через некоторый API). Число этих COM-объектов может быть ограниченным, поэтому мой класс реализует интерфейс IDisposable, который будет отвечать за освобождение COM-объекта, когда он больше не нужен.

  • Объекты другого типа ManagedObject временно создаются для выполнения некоторой работы над LimitedComResource. Они не IDisposable.

Подводя итог вышесказанному на диаграмме, мои классы могут выглядеть так:

            +---------------+           +--------------------+
            | ManagedObject | <>------> | LimitedComResource |
            +---------------+           +--------------------+
                                                  |
                                                  o IDisposable

(я приведу пример кода для этих двух классов через минуту.)

Вопрос:


Поскольку мои временные ManagedObject объекты не являются одноразовыми, я, очевидно, не могу контролировать, как долго они будут находиться вокруг. Тем не менее, в то же время у меня может быть Dispose d LimitedComObject, на который ссылается ManagedObject. Как я могу убедиться, что ManagedObject не получит доступ к LimitedComResource, которого больше нет?

            +---------------+           +--------------------+
            | managedObject | <>------> |   (dead object)    |
            +---------------+           +--------------------+

В настоящее время я реализовал это с помощью комбинации слабых ссылок и флага LimitedResource, который указывает, был ли объект уже удален. Есть ли лучший способ?

Пример кода (что у меня сейчас есть):


LimitedComResource

class LimitedComResource : IDisposable
{
    private readonly IUnknown comObject;  // <-- set in constructor

    ...

    void Dispose(bool notFromFinalizer)
    {
        if (!this.isDisposed)
        {
            Marshal.FinalReleaseComObject(comObject);
        }
        this.isDisposed = true;
    }

    internal bool isDisposed = false;
}

ManagedObject

class ManagedObject
{
    private readonly WeakReference limitedComResource;  // <-- set in constructor

    ...

    public void DoSomeWork()
    {
        if (!limitedComResource.IsAlive())
        {
            throw new ObjectDisposedException();
            //        ^^^^^^^^^^^^^^^^^^^^^^^
            //  is there a more suitable exception class?
        }

        var ur = (LimitedComResource)limitedComResource.Target;
        if (ur.isDisposed)
        {
            throw new ObjectDisposedException();
        }

        ...  // <-- do something sensible here!
    }
}

Ответы [ 2 ]

1 голос
/ 30 апреля 2010

Нет, это не хорошо. WeakReference говорит только о том, что управляемый объект получил мусор. Который не имеет ничего общего с IDisposable. Задача Dispose () - освободить неуправляемые ресурсы до того, как это сделает сборщик мусора.

На самом деле, у вас возникла серьезная проблема, если управляемый объект находится в поколении № 1, а оболочка COM - в поколении № 0. WeakReference не может поддерживать работу оболочки, она будет собрана, и COM-объект будет удален до того, как у вас появится возможность самостоятельно вызвать Dispose ().

Просто сохраните простую ссылку на объект-оболочку в вашем управляемом объекте. Вы можете установить его в null после вызова Dispose (), чтобы обертка могла быть собрана. И бросить ObjectDisposedException, если клиентский код пытается использовать его, и ссылка является нулевой. Или воссоздайте его, если это имеет смысл.

1 голос
/ 30 апреля 2010

Когда вы разыгрываете цель со слабой ссылкой на тип объекта, она вернет ноль, если объект был удален. Просто проверьте, является ли возвращаемое значение нулевым, прежде чем выполнять операции с ним. Смотрите пример в документации . Вы также можете найти эту статью на Использование слабых ссылок использования. Вот соответствующая цитата из последней статьи:

Чтобы установить сильную ссылку и использовать объект снова, наложить цель свойство WeakReference к тип объекта. Если цель свойство возвращает ноль, объект был собраны; в противном случае вы можете продолжить использовать объект, потому что приложение восстановило сильное ссылка на него.

Пример:

class ManagedObject 
{ 
    private readonly WeakReference limitedComResource;  // <-- set in constructor 

    ... 

    public void DoSomeWork() 
    { 
        var ur = (LimitedComResource)limitedComResource.Target; 
        if (ur == null) 
        { 
            throw new ObjectDisposedException(); 
        } 

        ...  // <-- do something sensible here! 
    } 
}
...