2013-02-02 2 views
1

나는 IDisposable 인터페이스를 가진 클래스가 있습니다. 지금 나는 어떤 행동을해야하는지 모른다. Dispose 메서드 후에이 클래스의 각 메서드 호출에 ObjectDisposedException을 던져야합니까, 아니면 폐기 된 리소스에 대한 데이터 액세스 같은 지정된 메서드에서만 예외를 throw해야합니까?처분 된 객체의 동작

Bitmap b = new Bitmap (100, 100); 
b.Dispose(); // if i remove this line - console will display: Format32bppArgb 
Console.WriteLine (b.PixelFormat); 
Console.ReadKey(); 

그리고 콘솔 표시 : 그래서 어떤 예외가 발생되지 않은 DontCare

나는 비트 맵 오브젝트 (단지 예)를 테스트했다. Bitmap 객체는 Dispose를 호출 한 후 PixelFormat 속성을 사용할 수 있습니다. 이 행동을 따라야합니까?

+0

방금 ​​버그를 찾아 내려고이 문제를 보았습니다. 'Bitmap' 객체 자체는 null이 아니지만 'Width' 나'Height'와 같은 속성을 참조하려고하면 ArgumentException을 던집니다. 유효한 유일한 속성은'PixelFormat'입니다. 여러분이 언급 한 "DontCare"입니다. 나는 그것의'Dispose()'을 수행 한 후에 객체를 null로 설정하지 않을 것입니다. 아주 이상한. 나는 심지어 GC.Collect()를 강제로이 문제에 성공시키지 않으려 고 노력했다. – Patrick

답변

1

이 문제와 다른 많은 문제에 대한 필자의 철학은 "합리적인 행동"입니다.

어떤 경우에는 클래스가 리소스를 해제 한 후에 특정 클래스 멤버가 사용되는 것이 매우 적절할 수 있습니다. 사실, 일부 시나리오에서는 그러한 사용이 필요할 수 있습니다. 예를 들어, 객체가 네트워크 연결을 통해 비동기 트랜잭션을 관리하는 경우 종료하도록 요청한 다음 처리가 완료된 트랜잭션 수, 매달 렸는지 여부 등을 물을 수 있습니다. 그러한 통계의 가치는 종료가 끝날 때까지 알 수 없으며 객체를 종료하고 이미 완료 한 작업과 관련된 기록 정보를 요구할 때 개념적으로 아무런 문제가 없습니다.

Dispose은 정보를보고하는 등록 정보를 허용하면서 Close은 연결을 종료해야한다고 주장하는 반면, 이러한 등록 정보의 사용을 허용하지 않으려면 이러한 구별을 도움이되지 않는 것으로 간주합니다. 무엇보다도 커넥션이 그 커넥션과 관련된 모든 리소스를 해제하기를 바랄 수도 있습니다 (무언가가 "재개"요청을 허용하기 위해 무언가를하지 않을 수도 있습니다). 또한 CloseDispose 사이에 다른 동작 차이가없는 경우 두 가지 별도의 방법을 필요로하지 않으므로 Dispose은 통계 데이터를 무효화 할 수 있습니다.

어떤 의미에서는 많은 개체가 외부 리소스와 상호 작용하는 엔터티와 관리되는 코드와 상호 작용하는 엔터티와 자체적으로 제한된 기능을 갖는 개체의 두 부분으로 간주 될 수 있습니다. "분리 된 관심사"원칙은 두 부분이 별도의 객체 여야 함을 암시하지만 (실제로 그러한 분할이 도움이 될 때도 있습니다.) 많은 경우 클라이언트 코드는 다음과 같은 경우에 하나의 참조를 보유하려고합니다. 두 목적을 다한다. 이 참조는 IDisposable을 구현해야하지만 처분이 관리 코드 측면을 파괴해서는 안됩니다.

예를 들어 WinForms Font 클래스를 생각해보십시오. 이 클래스는 두 가지를 캡슐화합니다. (1) 글꼴 (글자체, 크기, 스타일 등)에 대한 정보 모음 및 (2) GDI 글꼴 핸들.FontDispose d 일 때 더 이상 텍스트를 그리는 데 사용할 수 없지만 서체, 스타일 등을 잊지는 않습니다. Dispose d 글꼴이 주어지면 이전 글꼴의 정보를 사용하여 새 글꼴을 구성 할 수 있습니다 . 불행히도 이러한 정보를 읽을 수있는 대부분의 속성은 명시 적으로 Dispose에 의해 무효화됩니다. 즉, 기존에 배치되었지만 배치 된 Font과 유사하지만 약간의 변경 사항이있는 글꼴을 생성하려는 경우가 많다는 것을 의미합니다. 하나는 이전 글꼴에서 복사 된 정보로 새 글꼴을 만들고, 그 글꼴을 기반으로 다른 글꼴을 새로 만든 다음 Dispose 만든 첫 번째 새 글꼴을 만들어야합니다. 폰트에 대한 설명을 담고 싶지만 GDI 핸들을 필요로하지 않는 경우에는 폰트 설명이 a에 저장 될 수 있도록 typestyle 등과 관련된 정보를 보유한 FontDescription 클래스를 갖는 것이 도움이되었을 수 있습니다. 일회용이 아닌 클래스이지만 클래스가 설계된 방식이 아닙니다.

+0

클래스'DataBlock (long pSize, int pAlign = 16)'이 있습니다. 나는 다음과 같은 속성을 가진다 : Alignment {get; }, 크기 {get; }, 메소드는 다음과 같습니다 : EnterReadMode(), ExitReadMode() 등등. Aligment {get;}를 호출 할 때 예외를 사용해야합니까? 메모리 ID가 삭제되면 논리 배열과 크기가 없습니다. IsDisposed 속성을 구현하고 Exception을 throw해야하는 메서드를 잊어 버릴 필요가 있습니까? – zgnilec

+0

@zgnilec : 위의 편집을 참조하십시오. 나는 확실하게 IsDisposed를 추가 할 것이지만 확실히 당신의 타입에 관한 정보와 관련된 프로퍼티를 무효화시키지 않을 것입니다. 당신의 타입의 의미에 따라, 파기 된 인스턴스를 취할 수있는 생성자를 갖는 것과 비슷한 새로운 인스턴스를 생성하는 것이 도움이 될 수 있습니다 (단, 새로운 인스턴스는 삭제되지 않습니다) . – supercat

0

dispose를 호출 한 후 일반적으로 수행하는 접근 방식은 objectnull으로 설정하는 것입니다. 그런 다음 예외를 만들 필요가 없습니다. null 예외가 발생하고 적절한 방법 인 것 같습니다.

오브젝트가 널 (NULL) 인 경우, 처리 된 이후로 널 (null)인지 여부는 실제로 중요하지 않습니다. 또는 초기화되지 않았기 때문에 널 (null)이거나 널 (null)로 명시 적으로 설정되어 있으므로 널 (NULL)입니다. 소비자는 null이라는 기본 동작이 아니라 null임을 알아야합니다.

+0

그러나 코드의 여러 위치에서 일부 개체는 여전히 내 개체에 연결할 수 있습니다. – zgnilec

+0

@zgnilec 당신의 예제는 나를 위해 명확하지 않다, dispose를 호출 한 후에 질문에 b.PixelFormat에 값이 있다고 명시했다. 따라서 b는 가비지 컬렉터에 의해 수집 된 것 같지 않습니까? – daryal

+0

예, 픽셀 형식이 새 값을 반환합니다. - "DontCare". 이 코드는 예제 일뿐입니다. 질문 : IDisposable 인터페이스를 사용하여 클래스를 정의한 경우 Dispose 메서드를 호출 한 후이 클래스의 모든 메서드를 사용하지 않아야합니다. – zgnilec

1

은 폐기 된 리소스에 대한 데이터 액세스와 같은 지정된 메서드에서만 예외를 throw해야합니까?

자동 완성 기능이있는 클래스는 이와 같은 경우에 throw해야합니다. 결국이 메서드는 더 이상 존재하지 않는 운영 체제 개체에 액세스하려고합니다. 즉, 오류가 발생하게됩니다. 운영체제 오류 코드에 의해 생성 된 신비화 된 오류 대신 ObjectDisposedException과 같은 명확한 오류로보고하는 것이 더 좋습니다.

제공된 Bitmap 예제는 매우 슬픈 하나의 GDI + 클래스는 드문 일이 아닙니다. 일반적으로 오류 처리는 매우 열악합니다. 이것을 예가 아니라고합시다.

이전 단락의 핵심 구는 "finalizer가있는 클래스"입니다. 당신의 클래스는 이 아니어야합니다는 finalizer가 있습니다. 캡슐화하는 일회용 클래스의 메소드에 맡기지 않고 스스로 버리는 것이 든 논쟁의 여지가 있습니다. 일반적으로 그것을 피하는 것이 좋습니다, 거의 혼란스러워하는 경향이 있습니다. 그러나 잘못된 데이터를 반환하는 Bitmap과 같은 크 래미 클래스를 래핑하는 경우 자유롭게 수행하십시오.

+0

왜 파이널 라이저가 안되니? 나는 기사를 읽고, finalizer가 somtimes 필요하다는 것을 말한다. 내부적으로 GlobalHAlloc 호출을 사용하는 클래스 DataBlock이 있습니다. 때로는 1GB 이상의 메모리를 할당합니다.그래서 코드에 버그가 생겼습니다. Dispose를 호출하는 것을 잊었습니다.이 2GB의 데이터를 메모리에 보관하는 것은 매우 어려울 수 있습니다. – zgnilec

+0

예, 파이널 라이저가 필요합니다. 나는 당신이하지 않는 시대의 99.9 %에 관해 이야기하고있었습니다. –