Вызов удаления С++/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 обрабатывать его.