Правильная реализация IDisposable
Интерфейс, используемый для освобождения ресурсов объекта в .NET, — это IDisposable, но реализация этого интерфейса весьма специфична. Кроме того, есть две функции: Finalize и Close.
MSDN рекомендует реализовать интерфейс IDisposable по следующему шаблону:
1 общедоступный класс Foo: IDisposable.
2 {
3 публичная пустота Dispose()
4 {
5 Утилизировать (правда);
6 GC.SuppressFinalize(это);
7}
8
9 защищенных виртуальных пустот Dispose (удаление bool)
10 {
11, если (!m_dispose)
12 {
13 если (утилизация)
14 {
15 // Освобождение управляемых ресурсов
16}
17
18 // Освободить неуправляемые ресурсы
19
20 m_dispose = правда;
двадцать один }
двадцать два }
двадцать три
24 ~Фу()
25 {
26 Dispose(ложь);
27}
28
29 частное bool m_dispose;
30}
31
32
На самом деле существует две функции для освобождения ресурсов в объектах .NET: Dispose и Finalize. Целью Finalize является освобождение неуправляемых ресурсов, а Dispose используется для освобождения всех ресурсов, включая управляемые и неуправляемые.
В этом режиме функция void Dispose(bool dispositing) использует параметр dispose, чтобы определить, вызывается ли она в данный момент функцией Dispose(). Если он вызывается функцией Dispose(), управляемые и неуправляемые ресурсы необходимо освободить одновременно. Если он вызывается ~Foo() (то есть Finalize() C#), вам нужно освободить только неуправляемые ресурсы.
Это связано с тем, что функция Dispose() явно вызывается другим кодом и требует освобождения ресурсов, а функция Finalize вызывается сборщиком мусора. Другие управляемые объекты, на которые ссылается Foo, возможно, не нужно уничтожать при вызове GC, и даже если они будут уничтожены, они будут вызваны GC. Таким образом, в Finalize необходимо освободить только неуправляемые ресурсы. С другой стороны, поскольку управляемые и неуправляемые ресурсы освобождаются в Dispose(), нет необходимости снова вызывать Finalize, когда объект перерабатывается сборщиком мусора, поэтому вызовите GC.SuppressFinalize(this) в Dispose(), чтобы избежать дублирования. Завершить.
Однако проблем нет, даже если Finalize и Dispose вызываются неоднократно, поскольку из-за существования переменной m_dispose ресурс будет освобожден только один раз, а избыточные вызовы будут игнорироваться.
Таким образом, приведенный выше шаблон гарантирует:
1. Finalize освобождает только неуправляемые ресурсы.
2. Dispose выпускает управляемые и неуправляемые ресурсы.
3. Повторный вызов Finalize и Dispose не вызывает проблем.
4. Finalize и Dispose используют одну и ту же стратегию освобождения ресурсов, поэтому между ними нет конфликта; .
В C# этот шаблон необходимо реализовать явно, где функция C# ~Foo() представляет Finalize(). В C++/CLI этот режим реализуется автоматически, но деструктор класса C++ другой.
Согласно семантике C++, деструктор вызывается, когда он выходит за пределы области видимости или удаляется. В управляемом C++ (то есть управляемом C++ в .NET 1.1) деструктор эквивалентен методу Finalize() в CLR, который вызывается сборщиком мусора во время сборки мусора. Поэтому время вызова неясно. В C++/CLI в .NET 2.0 семантика деструкторов была изменена и стала эквивалентной методу Dispose(), что подразумевает две вещи:
1. Все классы CLR в C++/CLI реализуют интерфейс IDisposable, поэтому вы можете использовать ключевое слово using для доступа к экземплярам этого класса в C#.
2. Деструктор больше не эквивалентен Finalize().
Во-первых, это хорошо, я думаю, что семантически Dispose() ближе к деструктору C++. Что касается второго пункта, Microsoft расширила его, введя функцию «!», как показано ниже:
1 общедоступный класс ссылки Foo.
2 {
3 публичных:
4Фу();
5 ~Foo(); // деструктор
6 !Foo(); // финализатор
7};
8
Функция "!" (я действительно не знаю, как ее назвать) заменяет исходную Finalize() в Managed C++ и вызывается GC. MSDN рекомендует, чтобы уменьшить дублирование кода, писать такой код:
1 ~Foo()
2 {
3 //Освободить управляемые ресурсы
4 это->!Foo();
5}
6
7 !Фу()
8 {
9 //Освободить неуправляемые ресурсы
10}
11
Для приведенного выше класса соответствующий код C#, сгенерированный C++/CLI, на самом деле выглядит следующим образом:
1 общественный класс Foo
2 {
3 частная пустота !Foo()
4 {
5 // Освободить неуправляемые ресурсы
6}
7
8 частная пустота ~Foo()
9 {
10 // Освобождение управляемых ресурсов
11 !Фу();
12}
13
14 publicFoo()
15 {
16}
17
18 публичная недействительность Dispose()
19 {
20 Dispose(истина);
21 GC.SuppressFinalize(это);
двадцать два }
двадцать три
24 защищенных виртуальных пустот Dispose (удаление bool)
25 {
26 если (утилизация)
27 {
28 ~Фу();
29 }
еще 30
31 {
32 попытки
33 {
34 !Фу();
35}
36 наконец
37 {
38 base.Finalize();
39 }
40}
41 }
42
43 защищенная пустота Finalize()
44 {
45 Dispose(ложь);
46 }
47 }
48
Поскольку ~Foo() и !Foo() не будут вызываться повторно (по крайней мере, так считает MS), в этом коде нет переменных, аналогичных предыдущим m_dispose, но базовая структура та же самая.
Более того, вы можете видеть, что на самом деле это не ~Foo() и !Foo(), которые являются Dispose и Finalize, а компилятор C++/CLI генерирует две функции Dispose и Finalize и вызывает их в подходящее время. C++/CLI на самом деле проделал большую работу, но единственная проблема заключается в том, что он полагается на то, что пользователь вызывает !Foo() в ~Foo().
Что касается освобождения ресурса, последнее, о чем следует упомянуть, — это функция Close. Семантически она очень похожа на Dispose. Согласно MSDN, эта функция предназначена для того, чтобы пользователи чувствовали себя более комфортно, поскольку пользователи более привыкли вызывать Close() для определенных объектов, таких как файлы.
Однако, в конце концов, эти две функции делают одно и то же, поэтому MSDN рекомендует следующий код:
1 публичная пустота Close()
2 {
3 Удалить(();
4}
5
6
Здесь функция Dispose без параметров вызывается напрямую, чтобы получить ту же семантику, что и Dispose. Кажется, это идеально, но с другой стороны, если Dispose и Close будут предоставлены одновременно, это внесет некоторую путаницу у пользователей. Не видя деталей кода, трудно понять разницу между этими двумя функциями. Поэтому в спецификациях дизайна кода .NET говорится, что пользователи могут фактически использовать только одну из этих двух функций. Итак, предлагаемый шаблон:
1 общедоступный класс Foo: IDisposable.
2 {
3 публичная недействительность Close()
4 {
5 Удалить();
6}
7
8 void IDisposable.Dispose()
9 {
10 Удалить (правда);
11 GC.SuppressFinalize(это);
12}
13
14 защищенных виртуальных пустот Dispose (удаление bool)
15 {
16 // То же, что и раньше
17}
18}
19
Здесь используется так называемая явная реализация интерфейса: void IDisposable.Dispose(). Эта явная реализация доступна только через интерфейс, но не через реализующий класс. поэтому:
1 Foo foo = новый Foo();
2
3 foo.Dispose(); // Ошибка
4 (foo как IDisposable).Dispose(); // Правильно
5
Это заботится об обоих. Те, кто любит использовать Close, могут использовать foo.Close() напрямую, и они не увидят Dispose(). Те, кому нравится Dispose, могут преобразовать тип в IDisposable для вызова или использовать оператор using. Огромная радость обоим!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html