2009-04-01 5 views
2

다음의 IDisposable 패턴 구현에 대해 어떻게 생각하십니까?내 IDisposable 패턴 구현에 대해 어떻게 생각하십니까?

{ "동작이 소켓 이외의 개체에 작업을 시도했습니다"} System.Net.Sockets.Socket.Poll (INT32 :

public class Connection : IDisposable 
{ 
    private Socket _socket; 

    public bool IsConnected() 
    { 
     if (_socket.Poll(1, SelectMode.SelectRead) && _socket.Available == 0) 
      return false; 
     return true; 
    } 

    public void Disconnect() 
    { 
     if (m_socket != null && IsConnected()) 
     { 
      try 
      { 
       _socket.Shutdown(SocketShutdown.Both); 
       _socket.Disconnect(false); 
      } 
      catch (SocketException se) 
      { 
       System.Console.WriteLine(se.Message); 
      } 
     } 
    } 

    ~Connection() 
    { 
     Dispose(false); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (!IsConnected()) 
     { 
      if (disposing) 
      { 
       Disconnect(); 
      } 
      else 
      { 
       AppDomain currentDomain = AppDomain.CurrentDomain; 
       if (currentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted) 
       { 
        System.Console.WriteLine("Client failed to call Destroy"); 
       } 
      } 
     } 
    } 
} 

나는 위의 코드를 사용하여이 오류를 받았다 마이크로 초, 셀렉트 모드 모드)

+0

당신은 어떻게 생각하십니까? 조금 확장 할 수 있었습니까 ... 당신은 무엇을 기대 했습니까? 당신이 얻은 결과를 얻을 때 당신은 왜 supprosed입니까 ... –

+0

마무리 과정에서이 오류가 발생하지 않을 것으로 예상합니다. –

+0

IDisposable을 구현하고 있다면 이것을 편집해야합니다. 그것을 보여줄 질문. –

답변

11

구현에 심각한 결함이 있습니다. 당신은 실제로 IDisposable을 구현하지 않으며 가비지 수집기를 사용하여 리소스를 정리하게됩니다. 이는 나쁜 것입니다.

또한 GC가 돌아올 때 올바르게 정리하지도 않습니다 (제대로 수행하지만 실수로 실수 한 것입니다).

IDisposable을 구현하는 참조를 보유하고있는 경우 IDisposable을 구현하는 것은 수업의 책임입니다. 그런 다음 Dispose을 구현할 때 GC가 수행되지 않으면 (Dispose에 대한 명시 적 호출 인 경우) IDisposable 구현에 대해 Dispose으로 전화해야합니다.

Socket의 연결 상태를 확인했지만 그 결과로 Dispose을 호출하는 것과 같지 않습니다. 결과적으로 리소스가 누출됩니다 (결국 GC가이를 선택합니다). 제대로 IDisposable을 구현라는 제목의 MSDN 문서의 섹션을 참조 "구현의 Finalize 및 관리되지 않는 리소스를 정리하기 위해 폐기"하는 방법에 대한 가이드 라인에 대한

, 여기에 있습니다 :

http://msdn.microsoft.com/en-us/library/b1yfkh5e(VS.71).aspx

내가 있음을 유의 나는이 가이드 라인에 완전히 동의하지 않지만 가장 많이 채택되었습니다. 내 위치를 보려면 여기를 참조하십시오

우선 들어

http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable.aspx

+0

제발 ... 수정 제안 –

+0

그냥 casperOne의 포인트에 추가하면 일반적으로 이와 같은 클래스에서 파이널 라이저를 구현하지 않을 것입니다. –

2

, 왜 IDisposable를 구현하지 않습니다?

이렇게하면 실제로 사용자가 실제로 처리해야하는 개체를 사용자에게 명확하게 알릴 수 있습니다.

public class Connection : IDisposable 
{ 
    #region Dispose pattern 

    /// <summary> 
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 
    /// </summary> 
    /// <filterpriority>2</filterpriority> 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
     ReleaseUnmanagedResources(); 
     if (disposing) 
      ReleaseManagedResources(); 
    } 

    /// <summary> 
    /// Derived classes from this class should override this method 
    /// to clean up managed resources when Dispose is called. 
    /// </summary> 
    protected virtual void ReleaseManagedResources() 
    { 
     // Enter managed resource cleanup code here. 
    } 

    /// <summary> 
    /// Derived classes should override this method to clean up 
    /// unmanaged resources when Dispose is called. 
    /// </summary> 
    protected virtual void ReleaseUnmanagedResources() 
    { 
     // Enter unmanaged resource cleanup code here. 
    } 

    #endregion 
+0

IDisposable을 구현 중입니다 ... 죄송합니다.이 스 니펫에 포함을 잊어 버렸습니다. –

+0

그는 일종의 것입니다. IIRC는 Visual Studio에서 IDisposable이 생성하는 패턴과 매우 유사합니다. 그러나 어떤 이상한 이유 때문에 그는 컴파일러에게 그 부분에 대해 알려주는 부분을 제거하지 않기로 결정했기 때문에 패턴의 실질적인 이점을 취할 수는 없습니다. –

1

이 (나는이로 시작하는 게 좋을 것) 등 using 블록을 포장 할 수 이것은 리차드슨이 지적한 패턴으로 이어진다.

private bool disposed; 

private void Dispose(bool disposing) 
{ 
    if(!this.disposed) 
    { 
     ReleaseUnmanagedResources(); 
     if (disposing) 
      ReleaseManagedResources(); 
     this.disposed = true; 
    } 
} 
+0

파이널 라이저가없는 클래스에서 SuppressFinalize를 호출하는 이유는 무엇입니까? –

+0

필자는 결코 그것에 대해 생각해 보지 못했지만 GC를 호출 할 필요가 없다고 가정합니다 .SuppressFinalize는 파이널 라이저를 구현하지 않았다면 말입니다. 저 Earwicker에 전화해서 고마워. – heartlandcoder

1

난 그냥 추가 할 : 그들은 또한 그때는 일회용 패턴 과거에 사용되는 것을

+0

여전히 소멸자를 구현하겠습니까? –

3

이 구현에는 결함이있다.

먼저 Dispose() 메소드에는 socket.Dispose();을 호출하는 단일 용도가 있어야합니다. 지금 당장 당신은 소유하고있는 단일 IDisposable 리소스의 "Disposing"이 아니라 너무 많은 논리를 그곳에두고 있습니다.

둘째, 네이티브 관리되지 않는 리소스를 직접 소유하거나 할당하지 않기 때문에 두 번째로는 finalizer가 필요하지 않습니다. 처분 할 수있는 유일한 리소스는 Socket이며 관리되는 Socket은 필요에 따라 자체 finalizer를 구현합니다. 트랩을하고 Connection이 제대로 처리되지 않은 경우를 찾으려면 디버그 전용 finalizer를 설정하여이 경우를 경고하십시오. IDisposable의 finalizer는 호출자가 Dispose()를 호출하는 것을 잊었 기 때문에 GC가 정리 작업을 수행해야하는 경우를 처리하기위한 것이고, 사용자의 경우에는 소켓의 finalizer가 처리합니다.

세 번째로, Microsoft의 디자인 지침에 제안 된대로 IDisposable 패턴의 일부로 클라이언트가 결과없이 여러 번 Dispose()를 호출 할 수 있어야한다고 나와 있습니다. 사실, Dispose()는 socket.Close(); 또는 (socket as IDisposable).Dispose();을 호출하고 즉시 socket = null;을 호출하여이 문제를 방지해야한다고 제안합니다. 현재 논리를 사용하면 IsConnected()에있는 호출을 사용하면 이후에 Dispose()를 호출 할 때 소켓에서 예외가 발생하게되므로 피해야합니다.

넷째, 파일, 소켓 또는 기타 "닫을 수있는"리소스를 사용하는 모든 리소스에서 Close() 메서드를 사용하는 것이 좋습니다. Close()는 Dispose()를 호출해야합니다.

마지막으로 연결 사용에 대한 사후 처리 체크가 있어야합니다. 처리 후에 사용 된 연결의 모든 메소드는 ObjectDisposedException을 throw해야합니다.

+0

+1 아주 좋은 조언! –

+0

socket.Dispose() 보호 수준으로 인해 액세스 할 수 없습니다 .... –

+0

socket.Close()를 호출하거나 (IDisposable로 소켓) .Dispose()를 호출 할 수 있습니다. 둘 다 동일해야합니다. 소켓 IDisposable 명시 적으로 구현하지만 내가 말했듯이, "닫기"동작을 가진 모든 객체는 Close 또는 Dispose를 사용하여 동일한 결과를 가져야합니다. –

3

다른 답변에 명시 적으로 언급 된 것처럼 보이지 않으므로 구체적으로 잘못되어 가고있는 것에 관심이 있다면 여기를 참조하십시오.

두 객체에 파이널 라이저가 있고 GC에서 회수 할 때 파이널 라이저가 실행되는 순서는 없습니다. 코드에서 Socket 클래스에는 finalizer가 있고 클래스에는 finalizer가 있습니다. finalizer가 Socket 인스턴스에서 먼저 실행되면 finalizer가 실행될 때 최종화 된 객체에서 메서드를 호출하려고 시도하므로 예외가 발생합니다.

기본적으로 마무리 자. 비록 당신이 원시 Win32 핸들을 다루고있다하더라도, 대신에 SafeHandle을 사용하십시오.

대신 IDisposable을 구현하고 파이널 라이저를 작성하지 마십시오.

+0

+1, 이것은 사샤의 원래 질문에 대한 답변입니다. 나는 이것을 말하기 위해 나의 대답을 업데이트하려고했으나 당신은 그것에 나를 때렸다! – LukeH

1

마무리자는 고통이며 가능한 경우이라는 이 아니어야합니다. 그러나 IDisposable을 구현하면 거의 항상 Dispose (bool) 메서드와 함께 하나를 쓰고 싶을 것입니다. 그러면 Dispose (false) 및 GC.SuppressFinalize()가 호출됩니다.

finalizer를 구현하지 않으면 보유하고있는 IDisposable 인스턴스가 finalizer를 올바르게 구현하지 않으면 누출이 발생합니다. 예를 들어 SafeHandle에 마무리자가 없지만 Dispose를 호출하는 사람에게 의존했다고 가정 해 보겠습니다. 보유자를 보유하고 finalizer를 구현하지 않은 경우에는 Dispose를 호출하지 않아도 소비자가 영구적으로 핸들을 유출 할 수 있습니다.

당신이 소비하는 물건과 물건이 모두 즐겁다 고 생각하면 좋은 것을하지 않는 것이 아닙니다.

SafeHandle을 사용하는 경우에도 관리 대상인 것처럼 처리해야합니다.그렇지 않으면 당신이 그것을 처분하지 않으면 GC가 출시 될 때까지 기다려야 만합니다. 그리고 처분하지만 마무리 자도 없으면 아무도 처분하지 않습니다. 그러면 기다려야합니다. 당신을 모으고 나서 GC까지.

위의 b_richardson은 올바른 패턴을 가지고 있습니다. 어렵지 않습니다. GC.SuppressFinalize()를 추가하면 2 단계 GC를 피할 수 있습니다.

우리가 아직 처리했는지를 추적하기 위해 부울을 추가하는 경우, 작동 할 것입니다. 재사용 가능한 (Dispose) 메소드를 다시 작성하는 것이 좋습니다 (null 등을 확인하십시오). 방법은 객체가 사용 중에 자원을 획득 할 수 있고 그 객체가 가지고있는 자원만을 처분하려는 경우입니다.

+0

IDisposable을 구현하면 finalizer가 필요하다는 의미는 아닙니다. 이것은 정확하게 OP가 실행 된 문제입니다. 파이널 라이저가 완전히 잘못되었을 때 구현합니다. –

+0

나는 그것이 잘못 구현 된 때문이라고 말하고 싶습니다 ... IDisposable을 구현할 때 표준 파이널 라이저를 구현하지 않으면 관리되는 클래스가 참조를 보유하고 있다고 가정합니다. 이후 Dispose ... –

+0

SafeHandle을 사용하는 전체 목적 *은 사용자가 직접 최종자를 필요로하지 않는 것입니다. 일반적으로 모든 클래스에 파이널 라이저가 있어야만 파이널 라이 제이션에 필요하지 않은 것을 보관하는 것은 좋지 않은 생각입니다. – supercat