Вызов удаления С++/CLI на объекте С#
Я нахожусь в середине преобразования некоторого кода из С++/CLI в С#. Один из объектов имеет деструктор в версии С++/CLI. Некоторые другие коды С++/CLI вызывают "удаление" этого объекта после использования.
Какой метод мне нужно реализовать в версии С# этого объекта, чтобы эти "delete" продолжали работать одинаково (IDisposable.Dispose, финализатор или что-то еще, что мне не хватает)?
Ответы
Ответ 1
Я бы сказал, что интерфейс IDisposable
- это то, что вы ищете, если вам нужно детерминированное удаление ресурсов. Обычно это происходит с неуправляемыми ресурсами, такими как неуправляемые ручки, которые необходимо закрыть, потоки или подключения к базе данных.
В С++/CLI, если вы объявляете управляемый тип (ref class
и т.п.), IDisposable
реализуется с использованием синтаксиса деструктора, а Dispose()
вызывается с помощью ключевого слова delete
. Если вы объявляете такой объект управляемого типа локально (без использования оператора ^
или gcnew
), С++/CLI даже автоматически вызывает Dispose()
для вас, когда объект выходит из области видимости. Таким образом, С++/CLI более удобен, чем С#.
Вы не сможете вызвать delete
для объекта при использовании С#, вам нужно будет называть его Dispose()
вручную. Другой способ утилизации объектов IDisposable
- это using
.
Финализатор (реализованный в С# с использованием синтаксиса деструктора) не совпадает с деструктором С++, поскольку он не является детерминированным, когда он будет вызываться. Объекты с финализатором в основном ставятся в очередь до тех пор, пока поток финализатора не решит называть их финализатор, поэтому вы никогда не знаете точно, когда это вызвано.
Лучший подход к работе с неуправляемыми ресурсами, вероятно, является комбинацией этих двух. См. Здесь рекомендуемый подход:
http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.100).aspx
Обратите внимание, однако, что при использовании IDisposable
, хотя вы можете дестабилизировать распоряжение неуправляемыми ресурсами, управляемые объекты по-прежнему должны собираться сборщиком мусора (не детерминированным).
Я только что нашел статью, объясняющую различия между С++/CLI и С#. Вам может показаться интересным:
http://weblogs.thinktecture.com/cnagel/2006/04/ccli-finalize-and-dispose.html
Ответ 2
Там существует несоответствие терминологии между С++/CLI и С#. Деструктор С++/CLI (~ Foo) - это реализация метода IDisposable.Dispose(). Класс С++/CLI явно не реализует IDisposable, он является автоматическим только из наличия деструктора.
Финализатор С++/CLI (! Foo) является деструктором С#.
Таким образом, оператор удаления С++/CLI эквивалентен вызову метода Dispose(). Остерегайтесь семантики стека в С++/CLI, вызванной локальной переменной ссылочного типа без символа ^. Компилятор генерирует скрытый вызов Dispose() в конце блока области видимости. Это эквивалентно ключевому слову С#. Трудно видеть из исходного кода, так что обратите особое внимание или посмотрите на сгенерированный IL с помощью ildasm.exe
Ответ 3
С# не предоставляет те же инструменты, но предоставляет вам шаблоны:
Если ваш dtor закрывает поток, сглаживая указатели или устанавливая состояния соответствующим образом, я чувствую, что интерфейс IDisposable
подходит:
// C#
void Dispose()
{
_stream.Close();
_ptr = null;
_running = false;
}
// with
obj.Dispose();
Вы не можете заставить GC держать память открытой. Есть способы помочь GC узнать, что можно и нужно освободить, прочитайте http://msdn.microsoft.com/en-us/library/ee787088.aspx для получения дополнительной информации.
Имейте в виду, что использование IDisposable
предполагает, что вы вызовете метод Dispose()
соответствующим образом. Поэтому вместо каждого delete
вы хотите использовать Dispose()
. Безопасная часть финализатора заключается в том, что когда GC фактически освобождает его, он будет вызван. Однако вы не можете вызывать финализатор самостоятельно (поэтому, если важно время для удаления/удаления, это неправильное решение.)
Ответ 4
Для меня это действительно зависит от того, что делает деструктор. Если он делает что-то вроде освобождения неуправляемого ресурса (например, SQL или файловых подключений), я бы выполнил IDispose и закрою соединение в методе Dispose().
Если он уничтожает другие объекты, которые не требуют какой-либо явной очистки, я бы просто оставил деструктор и разрешил GC обрабатывать его.