IDisposable의 올바른 구현
.NET에서 개체 리소스를 해제하는 데 사용되는 인터페이스는 IDisposable이지만 이 인터페이스의 구현은 매우 특별합니다. 또한 Finalize 및 Close라는 두 가지 기능이 있습니다.
MSDN에서는 다음 패턴에 따라 IDisposable 인터페이스를 구현할 것을 권장합니다.
1 공용 클래스 Foo: IDisposable
2 {
3 공공 무효 Dispose()
4 {
5 처분(true);
6 GC.SuppressFinalize(this);
7}
8
9 보호된 가상 무효 처리(bool disposing)
10 {
11 if (!m_disposed)
12 {
13 if (처분)
14 {
15 // 관리형 리소스 해제
16}
17
18 // 관리되지 않는 리소스 해제
19
20m_disposed = 사실;
스물 하나 }
스물 둘 }
스물셋
24 ~푸()
25 {
26 처분(거짓);
27}
28
29 개인 bool m_disposed;
30}
31
32
실제로 .NET 개체에는 리소스를 해제하는 두 가지 함수인 Dispose와 Finalize가 있습니다. Finalize의 목적은 관리되지 않는 리소스를 해제하는 것이고 Dispose는 관리되는 리소스와 관리되지 않는 리소스를 포함한 모든 리소스를 해제하는 데 사용됩니다.
이 모드에서 void Dispose(bool disposing) 함수는 disposing 매개 변수를 사용하여 현재 Dispose()에 의해 호출되고 있는지 여부를 구별합니다. Dispose()에 의해 호출되는 경우 관리되는 리소스와 관리되지 않는 리소스를 동시에 해제해야 합니다. ~Foo()(즉, C#의 Finalize())에 의해 호출되는 경우 관리되지 않는 리소스만 해제하면 됩니다.
Dispose() 함수는 다른 코드에서 명시적으로 호출되어 리소스 해제가 필요한 반면 Finalize는 GC에서 호출되기 때문입니다. Foo가 참조하는 다른 관리 객체는 GC가 호출될 때 파기될 필요가 없을 수 있으며, 파기되더라도 GC에 의해 호출됩니다. 따라서 Finalize에서는 관리되지 않는 리소스만 해제하면 됩니다. 반면 관리되는 리소스와 관리되지 않는 리소스는 Dispose()에서 해제되었기 때문에 객체가 GC에 의해 재활용될 때 다시 Finalize를 호출할 필요가 없으므로 중복을 피하기 위해 Dispose()에서 GC.SuppressFinalize(this)를 호출합니다. 마무리합니다.
하지만 Finalize와 Dispose를 반복적으로 호출해도 문제가 되지 않습니다. m_disposed 변수의 존재로 인해 리소스는 한 번만 해제되고, 중복된 호출은 무시됩니다.
따라서 위 패턴은 다음을 보장합니다.
1. Finalize는 관리되지 않는 리소스만 해제합니다.
2. Dispose는 관리되는 리소스와 관리되지 않는 리소스를 해제합니다.
3. Finalize와 Dispose를 반복적으로 호출해도 문제가 없습니다.
4. Finalize와 Dispose는 동일한 리소스 해제 전략을 공유하므로 서로 충돌이 없습니다. .
C#에서는 이 패턴을 명시적으로 구현해야 합니다. 여기서 C#의 ~Foo() 함수는 Finalize()를 나타냅니다. C++/CLI에서는 이 모드가 자동으로 구현되지만 C++ 클래스 소멸자는 다릅니다.
C++ 의미 체계에 따르면 소멸자는 범위를 벗어나거나 삭제될 때 호출됩니다. Managed C++(즉, .NET 1.1의 관리되는 C++)에서 소멸자는 가비지 수집 중에 GC에 의해 호출되는 CLR의 Finalize() 메서드와 동일합니다. .NET 2.0의 C++/CLI에서는 소멸자의 의미 체계가 Dispose() 메서드와 동일하도록 수정되었습니다. 이는 다음 두 가지를 의미합니다.
1. C++/CLI의 모든 CLR 클래스는 IDisposable 인터페이스를 구현하므로 using 키워드를 사용하여 C#에서 이 클래스의 인스턴스에 액세스할 수 있습니다.
2. 소멸자는 더 이상 Finalize()와 동일하지 않습니다.
첫 번째 요점은 이것이 좋은 점입니다. 의미상 Dispose()가 C++ 소멸자에 더 가깝다고 생각합니다. 두 번째 사항에 대해 Microsoft는 아래와 같이 "!" 기능을 도입하여 확장을 만들었습니다.
1 public ref class Foo
2 {
3 공개:
4푸();
5 ~Foo(); // 소멸자
6 !Foo() // 종료자
7};
8
"!" 함수(실제로 뭐라고 불러야 할지 모르겠습니다)는 Managed C++의 원래 Finalize()를 대체하고 GC에 의해 호출됩니다. MSDN에서는 코드 중복을 줄이기 위해 다음과 같은 코드를 작성할 것을 권장합니다.
1 ~Foo()
2 {
3 //관리 리소스 해제
4 this->!Foo();
5}
6
7 !푸()
8 {
9 //관리되지 않는 리소스 해제
10}
11
위 클래스의 경우 C++/CLI에서 생성된 해당 C# 코드는 실제로 다음과 같습니다.
공개 클래스 1개 Foo
2 {
3 개인 무효 !Foo()
4 {
5 // 관리되지 않는 리소스 해제
6}
7
8 개인 공백 ~Foo()
9 {
10 // 관리형 리소스 해제
11 !푸();
12}
13
14 공개푸()
15 {
16}
17
18 공개 무효 Dispose()
19 {
20 처분(true);
21 GC.SuppressFinalize(this);
스물 둘 }
스물셋
24 보호된 가상 무효 처리(bool disposing)
25 {
26 if (처분)
27 {
28 ~푸();
29 }
그 외 30개
31 {
32번 시도
33 {
34 !푸();
35}
마침내 36
37 {
38 베이스.파이널라이즈();
39 }
40}
41 }
42
43 보호된 무효 Finalize()
44 {
45 처분(거짓);
46 }
47 }
48
~Foo() 및 !Foo()는 반복적으로 호출되지 않으므로(적어도 MS는 그렇게 생각함) 이 코드에는 이전 m_disposed와 동일한 변수가 없지만 기본 구조는 동일합니다.
게다가 실제로는 Dispose와 Finalize가 ~Foo()와 !Foo()가 아닌 것을 알 수 있는데, C++/CLI 컴파일러는 두 개의 Dispose와 Finalize 함수를 생성하여 적절한 시점에 호출합니다. C++/CLI는 실제로 많은 작업을 수행했지만 유일한 문제는 ~Foo()에서 !Foo()를 호출하는 사용자에 의존한다는 것입니다.
리소스 해제와 관련하여 마지막으로 언급할 것은 Close 기능입니다. MSDN에 따르면 이 기능은 파일과 같은 특정 개체에 대해 Close()를 호출하는 데 더 익숙하기 때문에 사용자가 더 편안하게 느낄 수 있도록 제공되는 기능입니다.
그러나 결국 이 두 함수는 동일한 작업을 수행하므로 MSDN에서 권장하는 코드는 다음과 같습니다.
1개의 공개 무효 닫기()
2 {
3 폐기(();
4}
5
6
여기서는 매개 변수 없이 Dispose 함수를 직접 호출하여 Dispose와 동일한 의미를 얻습니다. 이는 완벽해 보이지만, Dispose와 Close를 동시에 제공한다면 사용자에게 다소 혼란을 줄 수 있습니다. 코드 세부정보를 보지 않으면 이 두 기능의 차이점을 알기가 어렵습니다. 따라서 .NET 코드 디자인 사양에 따르면 사용자는 실제로 이 두 기능 중 하나만 사용할 수 있습니다. 따라서 제안된 패턴은 다음과 같습니다.
1 공개 클래스 Foo: IDisposable
2 {
3 공개 무효 닫기()
4 {
5 폐기();
6}
7
8 무효 IDisposable.Dispose()
9 {
10 처분(true);
11 GC.SuppressFinalize(this);
12}
13
14 보호된 가상 무효 처리(bool disposing)
15 {
16 // 이전과 동일
17}
18}
19
여기서는 인터페이스의 명시적 구현(void IDisposable.Dispose())이 사용됩니다. 이 명시적 구현은 인터페이스를 통해서만 액세스할 수 있으며 구현 클래스를 통해서는 액세스할 수 없습니다. 그러므로:
1 푸 foo = new Foo();
2
3 foo.Dispose(); // 오류
4 (IDisposable인 foo).Dispose(); // 정확함
5
이것은 두 가지를 모두 처리합니다. Close를 사용하려는 경우 foo.Close()를 직접 사용할 수 있으며 Dispose()는 표시되지 않습니다. Dispose를 좋아하는 사람들을 위해 유형을 IDisposable로 변환하여 호출하거나 using 문을 사용할 수 있습니다. 두 사람 모두에게 큰 기쁨이 됩니다!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html