IDisposable을 올바르게 구현


145

내 수업에서는 다음과 같이 IDisposable을 구현합니다.

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int UserID)
    {
        id = UserID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        // Clear all property values that maybe have been set
        // when the class was instantiated
        id = 0;
        name = String.Empty;
        pass = String.Empty;
    }
}

VS2012에서 코드 분석에 IDisposable을 올바르게 구현한다고 나와 있지만 여기서 내가 잘못한 일이 확실하지 않습니다.
정확한 텍스트는 다음과 같습니다.

CA1063 구현 IDisposable 'User'에서 무시할 수있는 Dispose (bool) 구현을 제공하거나 유형을 봉인 된 것으로 표시하십시오. Dispose (false)를 호출하면 기본 리소스 만 정리해야합니다. Dispose (true)를 호출하면 관리 자원과 기본 자원을 모두 정리해야합니다. stman User.cs 10

참조 : CA1063 : IDisposable을 올바르게 구현

이 페이지를 읽었지만 여기에서 수행해야 할 작업을 실제로 이해하지 못합니다.

누군가가 더 많은 라멘 용어로 문제가 무엇인지 또는 IDisposable을 어떻게 구현해야하는지 설명 할 수 있다면 정말 도움이 될 것입니다!


1
그 안에 모든 코드가 Dispose있습니까?
Claudio Redi

42
클래스의 멤버에서 Dispose () 메서드를 호출하려면 Dispose () 메서드를 구현해야합니다. 그 멤버 중 누구도 하나도 없습니다. 그러므로 당신은해야 되지 는 IDisposable을 구현합니다. 속성 값을 재설정하는 것은 의미가 없습니다.
한스 파 수트

13
당신은 구현해야 IDispoable당신이 (포장하는 관리되지 않는 리소스를 포함 (처분하는 관리되지 않는 리소스가있는 경우 SqlConnection, FileStream당신은하지 않으며, 등). 구현 IDisposable에만 자원을 관리 한 경우 등 여기로합니다.이 IMO입니다, 코드 분석의 주요 문제점 : 어리석은 규칙을 검사하는 데는 좋지만 개념적 오류를 검사하는 데는 좋지 않습니다
jason

51
어떤 사람들은 분명히 개념을 잘못 이해 한 사람을 돕는 것보다이 질문에 대해 공감하고이 질문이 종결되는 것을 보게되어 매우 화가납니다. 부끄러운 일이야
Ortund

2
따라서 공감하지 말고 공감하지 말고 게시물을 0으로 남겨두고 유용한 포인터로 질문을 닫으십시오.
tjmoore

답변:


113

게시 한 코드에서 처리해야 할 내용이 없지만 올바른 구현입니다. 다음과 같은 경우에만 구현 IDisposable하면됩니다.

  1. 관리되지 않는 리소스가 있습니다
  2. 당신은 스스로 처분 할 수있는 것들을 언급하고 있습니다.

게시 한 코드의 어떤 것도 폐기 할 필요가 없습니다.

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int userID)
    {
        id = userID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources if there are any.
    }
}

2
나는 C #으로 글을 쓰기 시작할 using(){ }때 가능한 한 항상 사용하는 것이 가장 좋다고 들었지만 IDisposable을 구현해야하므로 일반적으로 esp를 사용하여 클래스에 액세스하는 것을 선호합니다. 하나 또는 두 개의 기능으로 만 수업이 필요한 경우
Ortund

62
@Ortund 당신은 오해했습니다. 클래스가 IDisposable을 구현할 때using 블록 을 사용하는 것이 가장 좋습니다 . 일회용 클래스가 필요하지 않으면 구현하지 마십시오. 목적이 없습니다.
Daniel Mann

5
@DanielMann using블록 의 의미론은 IDisposable인터페이스만으로는 매력적이지 않습니다. 나는 IDisposable단지 범위 지정 목적으로 몇 가지 남용이 있었다고 생각 합니다.
토마스

1
관리되지 않는 리소스를 해제하려면 부수적으로 Dispose (false)를 호출하는 Finalizer를 포함시켜야합니다. 이렇게하면 가비지 수집 (Dispose가 아직 호출되지 않은 경우)을 수행 할 때 GC가 Finalizer를 호출하고 관리되지 않는 올바로 해제 할 수 있습니다. 자원.
mariozski

4
구현에 종결자가 없으면 호출 GC.SuppressFinalize(this);이 의미가 없습니다. @mariozski가 지적했듯이 finalizer는 클래스가 블록 내부에서 사용되지 않는 경우 호출 을 확인 하는 데 도움이됩니다 . Disposeusing
Haymo Kutschbach

57

우선, 당신은 "정리"할 필요가 없습니다 string들과 int의 - 그들은 가비지 컬렉터에 의해 자동으로 처리됩니다. 정리할 필요가있는 유일한 것은 Dispose관리되지 않는 리소스 또는 구현 된 관리 대상입니다 IDisposable.

그러나 이것이 단지 학습 연습이라고 가정 할 때 권장 되는 구현 방법 IDisposable은 "안전 캐치"를 추가하여 모든 자원이 두 번 폐기되지 않도록하는 것입니다.

public void Dispose()
{
    Dispose(true);

    // Use SupressFinalize in case a subclass 
    // of this type implements a finalizer.
    GC.SuppressFinalize(this);   
}
protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing) 
        {
            // Clear all property values that maybe have been set
            // when the class was instantiated
            id = 0;
            name = String.Empty;
            pass = String.Empty;
        }

        // Indicate that the instance has been disposed.
        _disposed = true;   
    }
}

3
+1, 클린업 코드가 한 번만 실행되도록하는 플래그를 갖는 것이 속성을 null 또는 무엇이든 (특히 readonly시맨틱 스 를 방해하기 때문에) 설정하는 것보다 낫습니다.
Thomas

사용자 코드를 사용하여 +1 (자동으로 정리 되기는하지만)로 이동하여 내용을 명확하게합니다. 또한, 짠 선원이 아니고 다른 많은 사람들과 마찬가지로 배우면서 작은 실수를 저지르는 그를 망치는 것에 대해.
Murphybro2

42

다음 예제는 IDisposable인터페이스 를 구현하는 일반적인 모범 사례를 보여줍니다 . 참고

수업에 관리되지 않는 리소스가있는 경우에만 소멸자 (완료 자)가 필요합니다. 소멸자를 추가하면 Dispose에서 Finalization을 억제해야합니다 . 그렇지 않으면 두 가비지주기 동안 객체가 메모리에 상주하게됩니다 (참고 : Finalization 작동 방식 읽기 ). 아래 예제는 위의 내용을 자세히 설명합니다.

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

14

IDisposable가비지 콜렉터가 자동으로 정리하지 않는 관리되지 않는 자원 을 정리할 수있는 수단을 제공하기 위해 존재합니다 .

"정리"하는 모든 자원은 관리 자원이며, 따라서 귀하의 Dispose방법은 아무것도 달성하지 못합니다. 수업이 전혀 구현되어서는 안됩니다 IDisposable. 가비지 콜렉터는 해당 필드를 모두 잘 처리합니다.


1
이것에 동의하십시오-실제로 필요하지 않을 때 모든 것을 처분한다는 개념이 있습니다. 정리할 관리되지 않는 리소스가있는 경우에만 폐기하십시오 !!
Chandramouleswaran Ravichandra

4
엄격하게 사실이 아니라면 Dispose 메소드를 사용하여 "IDisposable을 구현하는 관리 자원"을 폐기 할 수 있습니다.
Matt Wilko

@MattWilko 관리되지 않는 리소스를 간접적 으로 처리하는 방법으로 다른 리소스가 관리되지 않는 리소스를 처리 할 수 ​​있기 때문입니다. 여기에는 다른 관리 자원을 통해 관리되지 않는 자원에 대한 간접 참조 조차 없습니다 .
Servy

@MattWilko 폐기는 자동으로 IDesposable을 구현 관리 자원에서 호출됩니다
사람이에요 샤르마

@pankysharma 아니오, 그렇지 않습니다. 수동으로 호출 해야합니다 . 그것이 요점입니다. 자동으로 전화한다고 가정 할 수 는 없으며 사람들이 수동으로 전화 해야 한다는 것을 알고 있지만 사람들은 실수를하고 잊어 버립니다.
Servy

14

다음 과 같이 일회용 패턴 을 사용해야합니다 .

private bool _disposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            // Dispose any managed objects
            // ...
        }

        // Now disposed of any unmanaged objects
        // ...

        _disposed = true;
    }
}

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);  
}

// Destructor
~YourClassName()
{
    Dispose(false);
}

1
소멸자에서 GC.SuppressFinalize (this)를 호출하는 것이 현명하지 않습니까? 그렇지 않으면 객체 자체는 다음 GC에서 재생 될 것입니다
Sudhanshu Mishra

2
@dotnetguy : 오브젝트 소멸자는 gc가 실행될 때 호출됩니다. 따라서 두 번 전화하는 것은 불가능합니다. 여기 참조 : msdn.microsoft.com/en-us/library/ms244737.aspx
schoetbi 5

1
이제 상용구 코드를 "패턴"이라고 부릅니까?
Chel

4
@rdhs 아니요 우리는 아닙니다. MSDN 그것은 상태 입니다 여기에 패턴 "폐기 패턴"- msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx 그래서 전 구글은 조금 어쩌면 다운 투표?
Belogix

2
Microsoft 나 귀하의 게시물 모두 패턴이 왜 이런 모양이어야하는지 명확하게 설명하지 않았습니다. 일반적으로 보일러 플레이트가 아니며 불필요합니다- SafeHandle(및 하위 유형)으로 대체됩니다 . 적절한 처리를 구현하는 관리 자원의 경우 훨씬 단순 해집니다. void Dispose()메소드 의 간단한 구현으로 코드를 정리할 수 있습니다 .
BatteryBackupUnit

10

클래스 관리 되지 않는 리소스 (파일, 데이터베이스 연결 등)를 얻지 않으므로User 클래스 를 수행 할 필요 가 없습니다 . 일반적으로 클래스는 하나 이상의 필드 또는 속성 이있는 것처럼 표시 합니다. 구현할 때 Microsoft의 일반적인 체계에 따라 더 잘 작성하십시오.IDisposableIDisposableIDisposableIDisposable

public class User: IDisposable {
  ...
  protected virtual void Dispose(Boolean disposing) {
    if (disposing) {
      // There's no need to set zero empty values to fields 
      // id = 0;
      // name = String.Empty;
      // pass = String.Empty;

      //TODO: free your true resources here (usually IDisposable fields)
    }
  }

  public void Dispose() {
    Dispose(true);

    GC.SuppressFinalize(this);
  } 
}

그건 보통 의 경우. 그러나 반면에 using 구문은 C ++ 스마트 포인터와 비슷한 것을 작성할 가능성을 열어줍니다. 즉, using 블록이 종료되는 방식에 관계없이 이전 상태를 복원하는 객체입니다. 내가 찾은 유일한 방법은 그러한 객체를 IDisposable로 구현하는 것입니다. 그런 사소한 유스 케이스에서 컴파일러의 경고를 무시할 수있는 것 같습니다.
Papa Smurf

3

결정적 (확인 된) 가비지 수집을 원할 때마다 Idisposable을 구현합니다.

class Users : IDisposable
    {
        ~Users()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            // This method will remove current object from garbage collector's queue 
            // and stop calling finilize method twice 
        }    

        public void Dispose(bool disposer)
        {
            if (disposer)
            {
                // dispose the managed objects
            }
            // dispose the unmanaged objects
        }
    }

Users 클래스를 만들고 사용할 때 "pose"블록을 사용하여 dispose 메서드를 명시 적으로 호출하지 않도록합니다.

using (Users _user = new Users())
            {
                // do user related work
            }

작성된 using 블록의 끝 users 객체는 dispose 메서드의 암시 적 호출에 의해 처리됩니다.


2

실제로 안티 패턴 인 Microsoft Dispose 패턴의 많은 예를 봅니다. 많은 사람들이 질문에 지적했듯이 IDisposable이 전혀 필요하지 않습니다. 그러나 구현하려는 경우 Microsoft 패턴을 사용하지 마십시오. 더 나은 답변은이 기사의 제안을 따르는 것입니다.

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

도움이 될 수있는 유일한 다른 것은 코드 분석 경고를 억제하는 것입니다 ... https://docs.microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view=vs- 2017 년

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.