Как освобождать/удалять/уничтожать захваченные переменные в анонимных методах?

Я использую анонимные методы для обработки событий в COM-объекте. Как только программа завершается, оказывается, что ресурсы, которые я использую в анонимном методе, не закрываются правильно, поскольку я получаю исключение первого шанса (InvalidComObjectException) для каждого ресурса, который я наблюдал. Я полагаю, это не имеет большого значения, но это не кажется «правильным».

Я не могу понять, как получить доступ к этим захваченным переменным за пределами анонимных методов (которые вы в любом случае не должны делать). Как я могу закрыть/утилизировать ресурсы перед выходом из приложения?

РЕДАКТИРОВАТЬ: После краткого повторного чтения может быть неясно, что я здесь делаю. Я пишу управляемое приложение, которое использует COM-объект.

Дальнейшее редактирование: я использую ArcGIS Engine для управления данными ГИС. В данном конкретном случае я использую событие VisibilityChanged в ILayerEvents_Event, чтобы отслеживать, когда слой ГИС становится видимым или невидимым. Событие ТОЛЬКО передает логическое значение (видимый или невидимый), а НЕ имя слоя, поэтому необходимо создать метод для КАЖДОГО слоя, чтобы изменить его состояние видимости. Поскольку я имею дело с динамическими слоями, мне нужен был способ сделать это динамически, отсюда и анонимные методы.

Внутри метода anonymoua у меня есть переменная ILayer, которая получает ILayerEvents_Event из внешнего цикла (в контексте ILayer), чтобы я знал, с каким слоем я работаю. Именно в этот момент я застрял. Функциональность работает, и все замечательно, пока я не выхожу из приложения, оставляя эти 20+ ссылок висящими там, и им некуда идти, кроме исключения.

Я понятия не имею, когда пользователь будет скрывать/показывать слой в последний раз, поэтому нет никакого способа обнулить все в последний раз. Я полагаю, что могу просто оставить все как есть (или, возможно, есть лучший способ сделать это, чем анонимные методы), поскольку, похоже, это ничему не вредит. Я просто думаю, что я что-то упускаю.


person Michael Todd    schedule 27.05.2009    source источник
comment
Вам знакомо это чувство, когда вы работаете над чем-то без остановки и, наконец, у вас это получается? Я там. Спасибо всем за помощь (включая Марка Гравелла из отдельного поста). В итоге мне пришлось вести список COM-объектов и анонимных методов, которые были привязаны к их событиям, а затем отменять регистрацию делегатов перед завершением работы. Это заставило все работать без исключения.   -  person Michael Todd    schedule 28.05.2009


Ответы (3)


Не совсем возможно узнать это из того, как вы это описали, но, возможно, у вас есть COM-объект, который содержит ссылки на объекты C#, из-за того, что методы этих объектов зачислены в качестве обработчиков событий для события, представленного в COM-объекте, и когда COM-объект больше не используется, он завершается. Предполагая, что COM-объект основан на "квартире", это означает, что сообщение будет отправлено из потока финализатора в очередь сообщений Windows потока, создавшего COM-объект, с запросом на вызов Release для COM-объекта. В этот момент COM-объект, скорее всего, вызовет Release для объектов C#, реализующих любые зачисленные обработчики событий. Все это, вероятно, происходит, когда завершается выполнение последнего управляемого кода вашего приложения, и поэтому CLR пытается выгрузить себя. Возможно, во время завершения работы среда CLR должна справляться с любой возможной утечкой счетчика ссылок, позволяя собирать объекты или переводить их в недопустимое состояние до того, как их счетчики ссылок упадут до нуля. Так что это может (чистая гипотеза) объяснить то, что вы видите.

Если это так, вам нужно закрыть COM-объект в любое время по вашему выбору. Решение состоит в том, чтобы вызывать Marshal.ReleaseComObject в цикле до тех пор, пока он не вернет ноль для COM-объекта, как часть нормального завершения работы приложения.

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject(VS.71).aspx

Обновление на основе обновленного вопроса:

Итак, у вас есть некоторые COM-объекты, на которые ссылаются замыкания анонимных методов. Единственное, что произойдет с этими COM-объектами, — это то, что для них будет вызван Release. Если это вызывает проблему, скорее всего, это связано с тем, что они уже находятся в недопустимом состоянии.

Поэтому я бы рекомендовал, чтобы при создании набора замыканий анонимных методов, содержащих ссылки на COM-объекты, вы также добавляли эти COM-объекты в отдельный список. Это позволит вам вызывать Marshal.ReleaseComObject для них, когда вы отбрасываете всю эту систему.

person Daniel Earwicker    schedule 27.05.2009

Попробуйте использовать небезопасный режим в C#, создавайте вещи в куче и удаляйте их, когда закончите. Другая идея состоит в том, чтобы хранить ссылку на ресурсы в переменных, которые находятся вне анонимного метода, и корректно закрывать их при завершении программы. Опять же, все, что вам может понадобиться, это вызов Dispose().

Немного больше информации было бы полезно. Когда выбрасывается исключение? Что вы имеете в виду под ресурсами и "закрытыми правильно".

person Community    schedule 27.05.2009
comment
Я добавил немного больше информации выше. Как вы думаете, уместно ли что-то вроде ведения списка‹› объектов, на которые есть ссылки, а затем обработки их по завершении? - person Michael Todd; 28.05.2009

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

Для чего вы используете анонимный метод? Можете ли вы выяснить, когда он использовался в последний раз, и тогда избавиться от COM-объектов? Можно ли заставить анонимные методы получать COM-объекты и удалять их в одном и том же блоке?

Как видите, дьявол кроется в деталях :)

person Jon Skeet    schedule 27.05.2009
comment
Я добавил немного больше информации в вопрос. Любая помощь будет здорово. - person Michael Todd; 28.05.2009