Этот вопрос о контакте со (взаимодействующими с COM) неуправляемыми ресурсами и проверка, что там не будут никакие утечки ресурсов. Я ценил бы обратную связь на том, делаю ли я, кажется, вещи правильный путь.
Скажем, у меня есть два класса:
Класс LimitedComResource
который является оберткой вокруг COM-объекта (полученный через некоторый API). Может только быть ограниченное количество тех COM-объектов, поэтому мой класс реализует IDisposable
интерфейс, который будет ответственен за выпуск COM-объекта, когда он больше не будет необходим.
Объекты другого типа ManagedObject
временно создаются для выполнения некоторой работы над a LimitedComResource
. Они не IDisposable
.
Для суммирования вышеупомянутого в схеме мои классы могли бы быть похожими на это:
+---------------+ +--------------------+
| ManagedObject | <>------> | LimitedComResource |
+---------------+ +--------------------+
|
o IDisposable
(Я обеспечу пример кода для этих двух классов через только момент.)
Начиная с моего временного файла ManagedObject
объекты не доступны, я, очевидно, не имею никакого контроля, сколько времени они будут вокруг. Однако тем временем я мог бы иметь Dispose
d LimitedComObject
это a ManagedObject
относится к. Как я могу удостовериться это a ManagedObject
не получит доступ к a 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!
}
}
Нет, это не нормально. WeakReference только сообщает вам, что управляемый объект получил сборщик мусора. Что не имеет ничего общего с IDisposable. Задача Dispose () - освободить неуправляемые ресурсы до того, как это сделает сборщик мусора.
На самом деле, у вас серьезная проблема, если управляемый объект находится в поколении №1, а оболочка COM - в поколении №0. WeakReference не может поддерживать обертку в живых, она будет собрана, а COM-объект удален до того, как у вас появится возможность самостоятельно вызвать Dispose ().
Просто сохраните простую ссылку на объект-оболочку в вашем управляемом объекте. Вы можете установить для него значение null после вызова Dispose (), чтобы оболочка могла быть собрана. И выбросить ObjectDisposedException, если клиентский код пытается его использовать, а ссылка равна нулю. Или воссоздайте его, если в этом есть смысл.
Когда вы приводите цель слабой ссылки к типу объекта, она возвращает null, если объект был утилизирован. Просто проверьте, не является ли возвращаемое значение null, прежде чем выполнять с ним операции. Смотрите пример в документации. Возможно, вам также пригодится эта статья Использование слабых ссылок. Вот соответствующая цитата из последней статьи:
Чтобы установить сильную ссылку и использовать объект снова, приведите свойство Target свойства WeakReference к типу объекта. Если свойство Target возвращает null, объект был собран; в противном случае вы можете продолжать использовать объект, потому что приложение восстановило сильную ссылку на него.
Пример:
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!
}
}