Этот вопрос касается работы с неуправляемыми ресурсами (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!
}
}