Implementação correta de IDisposable
A interface usada para liberar recursos de objetos em .NET é IDisposable, mas a implementação desta interface é bastante particular. Além disso, existem duas funções: Finalizar e Fechar.
O MSDN recomenda implementar a interface IDisposable de acordo com o seguinte padrão:
1 public class Foo: IDisposable
2 {
3 Dispose vazio público ()
4 {
5 Descarte(verdadeiro);
6 GC.SuppressFinalize(este);
7}
8
9 vazio virtual protegido Dispose (descarte de bool)
10 {
11 se (!m_disposed)
12 {
13 se (descartando)
14 {
15 // Liberar recursos gerenciados
16}
17
18 // Liberar recursos não gerenciados
19
20 m_disposto = verdadeiro;
vinte e um }
vinte e dois }
vinte e três
24 ~Foo()
25 {
26 Dispor(falso);
27}
28
29 privado bool m_disposed;
30}
31
32
Na verdade, existem duas funções para liberar recursos em objetos .NET: Dispose e Finalize. O objetivo de Finalize é liberar recursos não gerenciados, enquanto Dispose é usado para liberar todos os recursos, incluindo gerenciados e não gerenciados.
Neste modo, a função void Dispose(bool disposing) usa um parâmetro disposing para distinguir se está sendo chamada por Dispose(). Se for chamado por Dispose(), os recursos gerenciados e não gerenciados precisarão ser liberados ao mesmo tempo. Se for chamado por ~Foo() (ou seja, Finalize() do C#), você só precisará liberar os recursos não gerenciados.
Isso ocorre porque a função Dispose() é explicitamente chamada por outro código e requer a liberação de recursos, enquanto Finalize é chamada pelo GC. Outros objetos gerenciados referenciados por Foo podem não precisar ser destruídos quando o GC for chamado e, mesmo que sejam destruídos, serão chamados pelo GC. Portanto, apenas os recursos não gerenciados precisam ser liberados no Finalize. Por outro lado, como os recursos gerenciados e não gerenciados foram liberados em Dispose(), não é necessário chamar Finalize novamente quando o objeto for reciclado pelo GC, então chame GC.SuppressFinalize(this) em Dispose() para evitar duplicação. Finalizar.
Porém, não há problema mesmo que Finalize e Dispose sejam chamados repetidamente, devido à existência da variável m_disposed, o recurso será liberado apenas uma vez e as chamadas redundantes serão ignoradas.
Portanto, o padrão acima garante:
1. Finalize apenas libera recursos não gerenciados;
2. Dispose libera recursos gerenciados e não gerenciados
3. Não há problema em chamar Finalize e Dispose repetidamente
; .
Em C#, esse padrão precisa ser implementado explicitamente, onde a função ~Foo() do C# representa Finalize(). Em C++/CLI, esse modo é implementado automaticamente, mas o destruidor de classe C++ é diferente.
De acordo com a semântica do C++, o destruidor é chamado quando sai do escopo ou é excluído. No C++ gerenciado (ou seja, C++ gerenciado no .NET 1.1), o destruidor é equivalente ao método Finalize() no CLR, que é chamado pelo GC durante a coleta de lixo. Portanto, o tempo da chamada não é claro. Em C++/CLI no .NET 2.0, a semântica dos destruidores foi modificada para ser equivalente ao método Dispose(), o que implica duas coisas:
1. Todas as classes CLR em C++/CLI implementam a interface IDisposable, então você pode usar a palavra-chave using para acessar instâncias desta classe em C#.
2. O destruidor não é mais equivalente a Finalize().
Para o primeiro ponto, isso é uma coisa boa, acho que semanticamente Dispose() está mais próximo do destruidor C++. Em relação ao segundo ponto, a Microsoft fez uma extensão introduzindo a função "!", conforme mostrado abaixo:
1 public ref class Foo
2 {
3 público:
4Foo();
5 ~Foo(); // destruidor
6 !Foo(); // finalizador
7};
8
A função "!" (realmente não sei como chamá-la) substitui o Finalize() original em Managed C++ e é chamada pelo GC. O MSDN recomenda que, para reduzir a duplicação de código, você possa escrever um código como este:
1 ~Foo()
2 {
3 //Liberar recursos gerenciados
4 isto->!Foo();
5}
6
7 !Foo()
8 {
9 //Liberar recursos não gerenciados
10}
11
Para a classe acima, o código C# correspondente gerado por C++/CLI é na verdade o seguinte:
1 aula pública Foo
2 {
3 vazio privado !Foo()
4 {
5 // Liberar recursos não gerenciados
6}
7
8 vazio privado ~Foo()
9 {
10 // Liberar recursos gerenciados
11 !Foo();
12}
13
14 públicoFoo()
15 {
16}
17
18 público vazio Dispose()
19 {
20 Dispor(verdadeiro);
21 GC.SuppressFinalize(este);
vinte e dois }
vinte e três
24 vazio virtual protegido Dispose (descarte de bool)
25 {
26 se (descartando)
27 {
28 ~Foo();
29}
30 mais
31 {
32 tentar
33 {
34 !Foo();
35}
36 finalmente
37 {
38 base.Finalize();
39}
40}
41}
42
43 finalização nula protegida()
44 {
45 Descarte(falso);
46}
47}
48
Como ~Foo() e !Foo() não serão chamados repetidamente (pelo menos a MS pensa assim), não há variáveis neste código que sejam iguais ao m_disposed anterior, mas a estrutura básica é a mesma.
Além disso, você pode ver que na verdade não são ~Foo() e !Foo() que são Dispose e Finalize, mas o compilador C++/CLI gera duas funções Dispose e Finalize e as chama no momento apropriado. Na verdade, C++/CLI fez muito trabalho, mas o único problema é que ele depende da chamada do usuário! Foo() em ~Foo().
Em relação à liberação de recursos, a última coisa a mencionar é a função Fechar. É semanticamente muito semelhante ao Dispose. De acordo com o MSDN, esta função é fornecida para deixar os usuários mais confortáveis, pois os usuários estão mais acostumados a chamar Close() para determinados objetos, como arquivos.
Porém, afinal, essas duas funções fazem a mesma coisa, então o código recomendado pelo MSDN é:
1 vazio público Fechar()
2 {
3 Descarte(();
4}
5
6
Aqui, a função Dispose sem parâmetros é chamada diretamente para obter a mesma semântica de Dispose. Isso parece perfeito, mas por outro lado, se Dispose e Close forem fornecidos ao mesmo tempo, causará alguma confusão aos usuários. Sem ver os detalhes do código, é difícil saber a diferença entre essas duas funções. Portanto, as especificações de design do código .NET dizem que os usuários só podem usar uma dessas duas funções. Portanto o padrão sugerido é:
1 classe pública Foo: IDisposable
2 {
3 público vazio Fechar()
4 {
5 Dispor();
6}
7
8 vazio IDisposable.Dispose()
9 {
10 Dispor(verdadeiro);
11 GC.SuppressFinalize(este);
12}
13
14 vazio virtual protegido Dispose (descarte de bool)
15 {
16 // Igual a antes
17}
18}
19
Uma chamada implementação explícita da interface é usada aqui: void IDisposable.Dispose(). Esta implementação explícita só é acessível através da interface, mas não através da classe de implementação. portanto:
1 Foo foo = novo Foo();
2
3 foo.Dispose(); // Erro
4 (foo como IDisposable).Dispose();
5
Isso cuida de ambos. Para quem gosta de usar Close, pode usar foo.Close() diretamente e não verá Dispose(). Para quem gosta de Dispose, ele pode converter o tipo para IDisposable para chamar, ou usar a instrução using. Muita alegria para ambos!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html