C #에서 Finalize / Dispose 메서드 사용


381

C # 2008

나는이 문제에 대해 잠시 동안 노력해 왔으며 여전히 코드에서 finalize 및 dispose 메서드를 사용하는 것에 대해 혼란스러워합니다. 내 질문은 다음과 같습니다.

  1. 관리되지 않는 리소스를 처리하는 동안 종료자가 필요하다는 것을 알고 있습니다. 그러나 관리되지 않는 리소스를 호출하는 관리되는 리소스가있는 경우 여전히 종료자를 구현해야합니까?

  2. 그러나 관리되지 않는 리소스를 직접 또는 간접적으로 사용하지 않는 클래스를 개발하는 경우 IDisposable해당 클래스의 클라이언트가 'using statement'를 사용할 수 있도록를 구현해야 합니까?

    클래스의 클라이언트가 using 문을 사용할 수 있도록 IDisposable을 구현하는 것이 가능합니까?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
  3. Finalize / dispose 사용을 보여주기 위해 아래의 간단한 코드를 개발했습니다.

    public class NoGateway : IDisposable
    {
        private WebClient wc = null;
    
        public NoGateway()
        {
            wc = new WebClient();
            wc.DownloadStringCompleted += wc_DownloadStringCompleted;
        }
    
    
        // Start the Async call to find if NoGateway is true or false
        public void NoGatewayStatus()
        {
            // Start the Async's download
                // Do other work here
            wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
        }
    
        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // Do work here
        }
    
        // Dispose of the NoGateway object
        public void Dispose()
        {
            wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
            wc.Dispose();
            GC.SuppressFinalize(this);
        }
    }

소스 코드에 대한 질문 :

  1. 여기서는 종료자를 추가하지 않았으며 일반적으로 GC에서 종료자를 호출하고 종료자가 Dispose를 호출합니다. 파이널 라이저가 없으므로 Dispose 메서드를 언제 호출해야합니까? 그것을 호출 해야하는 클래스의 클라이언트입니까?

    따라서 예제에서 내 클래스는 NoGateway라고하며 클라이언트는 다음과 같이 클래스를 사용하고 폐기 할 수 있습니다.

    using(NoGateway objNoGateway = new NoGateway())
    {
        // Do stuff here   
    }

    실행이 using 블록의 끝에 도달하면 Dispose 메소드가 자동으로 호출됩니까? 아니면 클라이언트가 dispose 메소드를 수동으로 호출해야합니까? 즉

    NoGateway objNoGateway = new NoGateway();
    // Do stuff with object
    objNoGateway.Dispose(); // finished with it
  2. WebClient수업에서 수업을 사용하고 NoGateway있습니다. 인터페이스를 WebClient구현 하기 때문에 관리되지 않는 리소스 IDisposableWebClient간접적으로 사용 한다는 의미 입니까? 이것을 따라야하는 어렵고 빠른 규칙이 있습니까? 클래스가 관리되지 않는 리소스를 사용한다는 것을 어떻게 알 수 있습니까?


1
이 리소스 릴리스 문제를 해결하기 위해이 복잡한 디자인 패턴이 실제로 필요한가?
zinking

답변:


422

권장되는 IDisposable 패턴은 여기에 있습니다 . IDisposable을 사용하는 클래스를 프로그래밍 할 때는 일반적으로 두 가지 패턴을 사용해야합니다.

관리되지 않는 리소스를 사용하지 않는 봉인 클래스를 구현할 때는 일반 인터페이스 구현과 마찬가지로 Dispose 메서드를 구현하면됩니다.

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

봉인되지 않은 클래스를 구현할 때는 다음과 같이하십시오.

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

B;에 종료 자를 선언하지 않았습니다 . 처리 할 실제 관리되지 않는 리소스가있는 경우에만 종료자를 구현해야합니다. CLR SuppressFinalize은 호출 가능 하더라도 최종화 가능 오브젝트를 최종화 불가능 오브젝트와 다르게 처리합니다 .

따라서 필요한 경우가 아니라면 종료자를 선언해서는 안되지만 클래스 상속자에게 Dispose관리되지 않는 리소스를 직접 사용하는 경우 호출자를 호출 하고 종료 자를 직접 구현할 수있는 후크를 제공해야합니다 .

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

관리되지 않는 리소스를 직접 사용하지 않는 경우 ( SafeHandle친구가 자신의 종료자를 선언하므로 계산에 포함되지 않음), GC는 나중에 종료자를 제외하더라도 종료 자 클래스를 다르게 처리하므로 종료자를 구현하지 않습니다. 또한 Bfinalizer가 없지만 finalizer를 SuppressFinalize구현하는 하위 클래스를 올바르게 처리하도록 호출 합니다.

클래스가 IDisposable 인터페이스를 구현할 때 클래스 사용이 끝나면 제거해야 할 관리되지 않는 리소스가있는 곳을 의미합니다. 실제 자원은 클래스 내에 캡슐화됩니다. 명시 적으로 삭제할 필요는 없습니다. Dispose()클래스를 단순히 호출 하거나 래핑하면 using(...) {}관리되지 않는 리소스가 필요에 따라 제거됩니다.


26
나는 thecoop에 동의합니다. 관리되는 리소스 만 처리하는 경우에는 종료자가 필요하지 않습니다 (실제로 "this"이외의) 종료 자 내에서 관리되는 개체에 액세스하려고하면 순서가 보장되지 않으므로 종료하지 않아도됩니다. GC는 개체를 정리할뿐만 아니라 .Net 2.0 이상을 사용하는 경우 SafeHandles를 사용하여 관리되지 않는 핸들을 래핑 할 수 있으며 관리 핸들에 대한 종료자를 작성할 필요성을 크게 줄입니다 ( blogs.msdn). com / bclteam / archive / 2005 / 03 / 16 / 396900.aspx
JMarsch

5
일회용 객체는 항상 폐기해야하기 때문에 종료 자에서 MessageBox.Show ( "Error,"+ GetType (). Name + "not 폐기")를 호출하는 것이 좋습니다. 가능한 빨리 사실을 알리는 것이 가장 좋습니다.
erikkallen

95
@erikkallen 그게 장난이야? :)
Alex Norcliffe

2
CLR에서 추가적인 컴퓨팅 노력이 필요하기 때문에 액티브 파이널 라이저로 클래스를 추적 할 수 있습니다. -종료자를 구현하면이 문제가 발생합니다. GC.SuppressFinalize를 호출하면 런타임에서 Finalizer를 호출하면 안됩니다. 여전히 Gen2에 관계없이 진행됩니다. 관리되는 리소스를 다루지 않는 경우 종료자를 추가하지 마십시오. 봉인 또는 봉인되지 않은 클래스 수정자는 해당 시점과 관련이 없습니다.
Ritch Melton

3
@Ritch : 인용? 반드시 나쁜 것은 아닙니다. 을 구현 IDisposable하는 경우 어쨌든 잠시 동안 중단 될 수 있습니다. CLR을 Gen0-> Gen1-> Gen2에서 복사해야하는 노력을 절약하고 있습니다.
thecoop

123

구현할 공식 패턴 IDisposable 은 이해하기 어렵습니다. 나는 이것이 더 낫다고 믿는다 .

public class BetterDisposableClass : IDisposable {

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

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

더 나은 솔루션은하는 규칙을 가지고있다 항상 당신이 처리해야하는 관리되지 않는 자원에 대한 래퍼 클래스를 만들어야합니다 :

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

}

SafeHandle 파생 된 클래스는 매우 드 물어야 합니다.

상속이 있더라도 관리되지 않는 리소스를 직접 처리하지 않는 일회용 클래스의 결과는 강력합니다. 더 이상 관리되지 않는 리소스에 대해 걱정할 필요가 없습니다 . 구현하고 이해하는 것이 간단 합니다.

public class ManagedDisposable : IDisposable {

  public virtual void Dispose() {
    // dispose of managed resources
  }

}

@Kyle : 감사합니다! 나도 좋아 :-) 여기에 후속 조치가 있습니다 .
Jordão

4
내가 주목하고 싶은 것은 두 번째로 호출되는 것을 막지 못한다는 것입니다.
HuseyinUslu

5
@HuseyinUslu : 패턴 의 본질 입니다. 반드시 disposed플래그를 추가하고 그에 따라 확인할 수 있습니다 .
Jordão

2
@didibus : disposed플래그 를 추가하는 간단한 문제입니다 . 처분하기 전에 확인하고 처분 한 후에 설정하십시오. 아이디어를 찾으 십시오 . 또한 클래스의 메소드 전에 플래그를 확인해야합니다. 맞는 말이다? 복잡합니까?
Jordão

1
+1 "더 좋은 솔루션은 항상 당신이 처리해야하는 관리되지 않는 리소스에 대한 래퍼 클래스를 생성해야한다는 규칙을 가지고하는 것입니다" . VLC의 애드온 에서이 문제를 발견했으며 그 이후로 사용 해 왔습니다. 너무 많은 두통
Franz B.

37

IDisposable 구현은 아래 패턴 (IMHO)을 따라야합니다. 나는 몇 가지 뛰어난 .NET "신들"의 정보를 기반으로이 패턴을 .NET Framework 설계 지침 (MSDN은 어떤 이유로 든 따르지 않음에 유의)을 개발했다. .NET Framework 디자인 지침은 Krzysztof Cwalina (당시 CLR 설계자)와 Brad Abrams (당시 CLR 프로그램 관리자라고 생각 함) 및 Bill Wagner ([Effective C #] 및 [More Effective C #])가 작성했습니다. Amazon.com에서 다음을 찾으십시오.

클래스에 관리되지 않는 리소스를 직접 포함 (상속하지 않음)하지 않는 한 Finalizer를 구현해서는 안됩니다. 클래스에서 Finalizer를 구현 한 후에는 호출되지 않더라도 추가 컬렉션을 위해 라이브를 보장합니다. 단일 스레드에서 실행되는 Finalization Queue에 자동으로 배치됩니다. 또한 매우 중요한 참고 사항 ... Finalizer 내에서 실행되는 모든 코드 (하나를 구현해야 함)는 스레드 안전하고 예외 안전해야합니다! 나쁜 일은 그렇지 않으면 일어날 것입니다 ... (즉, 결정되지 않은 동작과 예외의 경우 치명적인 복구 할 수없는 응용 프로그램 충돌).

내가 결합하고 코드 스 니펫을 작성한 패턴은 다음과 같습니다.

#region IDisposable implementation

//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable

// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }

/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
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.

    // Always use SuppressFinalize() in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize( this );
}

/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> 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.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
    // TODO If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    try
    {
        if( !this.IsDisposed )
        {
            if( isDisposing )
            {
                // TODO Release all managed resources here

                $end$
            }

            // TODO Release all unmanaged resources here



            // TODO explicitly set root references to null to expressly tell the GarbageCollector
            // that the resources have been disposed of and its ok to release the memory allocated for them.


        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );

        this.IsDisposed = true;
    }
}

//TODO Uncomment this code if this class will contain members which are UNmanaged
// 
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
//  ~$className$()
//  {
//     Dispose( false );
//  }
#endregion IDisposable implementation

다음은 파생 클래스에서 IDisposable을 구현하기위한 코드입니다. 파생 클래스 정의에 IDisposable의 상속을 명시 적으로 나열 할 필요는 없습니다.

public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)


protected override void Dispose( bool isDisposing )
{
    try
    {
        if ( !this.IsDisposed )
        {
            if ( isDisposing )
            {
                // Release all managed resources here

            }
        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );
    }
}

이 구현을 내 블로그에 게시했습니다. 폐기 패턴을 올바르게 구현하는 방법


사람은 (이 기본 클래스에서 파생) 파생 클래스에 대한 패턴을 추가 할 수
akjoshi

3
@akjoshi-파생 일회용 클래스의 코드를 포함하도록 위의 패턴을 업데이트했습니다. 또한 파생 클래스에서 Finalizer를 구현하지 마십시오.
Dave Black

3
Microsoft는 폐기 된 메소드의 끝에 "disposed"플래그를 설정하는 것을 좋아하지만, 나에게는 잘못된 것 같습니다. "Dispose"에 대한 중복 호출은 아무 것도하지 않아야합니다. 일반적으로 Dispose가 재귀 적으로 호출되는 것을 기대하지는 않지만, 구성 또는 다른 작업 중에 발생한 예외로 인해 유효하지 않은 상태로 남아있는 객체를 처리하려고하면 이러한 문제가 발생할 수 있습니다. 비가 상 래퍼 함수에서 Interlocked.Exchange정수 IsDisposed플래그를 사용하는 것이 더 안전하다고 생각합니다.
supercat

@DaveBlack : 기본 클래스가 관리되지 않는 리소스를 사용하지 않지만 파생 클래스는 사용하는 경우 어떻게됩니까? 그러면 파생 클래스에서 Finalizer를 구현해야합니까? 그렇다면 소스에 액세스 할 수없는 경우 기본 클래스가 이미 구현하지 않았다는 것을 어떻게 알 수 있습니까?
Didier A.

@DaveBlack "저는 훌륭한 .NET"신들 "의 정보를 바탕으로이 패턴을 개발했습니다."신들 중 하나가 Jon Skeet이라면 나는 당신의 조언을 따를 것입니다.
엘리자베스

23

pm100에 동의합니다 (이전 게시물에서이를 명시 적으로 말 했어야 함).

필요한 경우가 아니면 클래스에서 IDisposable을 구현해서는 안됩니다. 구체적으로 말하면 IDisposable을 구현해야 할 때 약 5 배가 있습니다.

  1. 클래스에는 IDisposable을 구현하고 클래스를 더 이상 사용하지 않으면 정리해야하는 관리 자원이 명시 적으로 포함됩니다 (상속을 통해 상속되지 않음). 예를 들어 클래스에 Stream, DbCommand, DataTable 등의 인스턴스가 포함 된 경우

  2. 클래스에는 Close () 메서드를 구현하는 관리 리소스 (예 : IDataReader, IDbConnection 등)가 명시 적으로 포함되어 있습니다. 이러한 클래스 중 일부는 Dispose () 및 Close () 메서드를 통해 IDisposable을 구현합니다.

  3. 클래스에는 명시 적으로 관리되지 않는 리소스 (예 : COM 개체, 포인터)가 포함됩니다 (예, 관리되는 C #에서 포인터를 사용할 수 있지만 '안전하지 않은'블록으로 선언해야합니다.) 관리되지 않는 리소스의 경우에는 RCW에서 System.Runtime.InteropServices.Marshal.ReleaseComObject ()를 호출합니다. 이론적으로 RCW는 관리되는 래퍼이지만 여전히 커버 아래에서 참조 카운트가 발생합니다.

  4. 클래스가 강력한 참조를 사용하여 이벤트를 구독하는 경우 이벤트에서 등록을 해제하거나 분리해야합니다. 등록 해제 / 분리를 시도하기 전에 항상 null이 아닌지 확인하십시오!.

  5. 수업에는 위의 조합이 포함되어 있습니다 ...

COM 개체로 작업하고 Marshal.ReleaseComObject ()를 사용하는 대신 권장되는 대안은 System.Runtime.InteropServices.SafeHandle 클래스를 사용하는 것입니다.

BCL (Base Class Library Team)에는 http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx에 대한 블로그 게시물이 있습니다.

WCF로 작업하고 리소스를 정리하는 경우 항상 '사용'블록을 피해야한다는 점에 유의해야합니다. MSDN에 블로그 게시물이 많으며 이것이 왜 나쁜 생각인지에 대한 내용이 있습니다. 나는 또한 그것에 대해 게시 했다-WCF 프록시와 함께 'using ()'을 사용하지 마십시오


3
다섯 번째 경우가 있다고 생각합니다. 클래스가 강력한 참조를 사용하여 이벤트를 구독하는 경우 IDisposable을 구현하고 Dispose 메서드의 이벤트에서 자신을 등록 취소해야합니다.
Didier A.

안녕하세요 디디 부스. 그래 정확 해. 나는 그 것을 잊었다. 사례를 포함하도록 답변을 수정했습니다. 감사.
Dave Black

dispose 패턴에 대한 MSDN 설명서에는 다음과 같은 또 다른 사례가 추가되었습니다. "관리되지 않는 리소스 나 일회용 개체를 보유하지 않지만 하위 유형이있을 수있는 클래스에 기본 Dispose 패턴을 구현하는 것을 고려하십시오. 이에 대한 좋은 예는 System.IO입니다. .Stream 클래스. 리소스를 보유하지 않는 추상 기본 클래스이지만 대부분의 서브 클래스가이를 수행하기 때문에이 패턴을 구현합니다. "
Gonen I

12

IDisposable 대신 람다 사용.

나는 전체 사용 / IDisposable 아이디어에 대해 결코 흥분하지 않았습니다. 문제는 호출자가 다음을 수행해야한다는 것입니다.

  • 그들이 IDisposable을 사용해야한다는 것을 안다
  • '사용'을 사용해야합니다.

내가 선호하는 새로운 방법은 팩토리 메소드와 람다를 대신 사용하는 것입니다.

SqlConnection으로 무언가를하고 싶다고 상상해보십시오 (사용에 싸여 있어야 함). 고전적으로 당신은 할 것입니다

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

새로운 길

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

첫 번째 경우 호출자는 단순히 using 구문을 사용할 수 없습니다. 두 번째 경우에는 사용자가 선택할 수 없습니다. SqlConnection 개체를 만드는 메서드가 없으므로 호출자는 DoWithConnection을 호출해야합니다.

DoWithConnection은 다음과 같습니다

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection 이제 비공개입니다


2
람다로 물건을 감싸는 것은 좋은 접근법 일 수 있지만 한계가 있습니다. 실제로 클래스의 모든 소비자가 "using"블록을 사용하는 상황에는 그렇게 나쁘지는 않지만 메소드가 클래스 필드에 직접 또는 반복자와 같은 IDisposable을 저장하는 상황은 허용하지 않습니다. ).
supercat

@supercat 당신은 자원을 낭비하는 것들의 저장을 금지하는 것이 좋은 것이라고 주장 할 수 있습니다. 여기에서 제안하는 차용 모델은 자원 사용에
의존하도록 강요

좋은 일이 될 수 있지만 매우 합리적인 작업을 매우 어렵게 만들 수도 있습니다. 예를 들어, IEnumerable <T>을 구현하는 대신 데이터베이스 판독기 유형이 DoForAll(Action<T>) where T:IComparable<T>각 레코드에서 표시된 대리자를 호출 하는 메소드를 노출한다고 가정하십시오 . 두 개의 객체가 모두 정렬 된 순서로 데이터를 반환 할 경우 한 컬렉션에 존재하지만 다른 컬렉션에는없는 모든 항목을 어떻게 출력합니까? 유형이 구현 IEnumerable<T>되면 병합 작업을 수행 할 수는 있지만 작동하지 않습니다 DoForAll.
supercat December

DoForAll하나의 전체를 다른 구조로 복사하지 않고 두 컬렉션 을 병합하는 유일한 방법은 두 개의 스레드를 사용하는 것입니다. 이는 두 개의 스레드를 사용하는 것입니다. 그들을 풀어주기 위해.
supercat December

-1 : 묻지 않은 질문에 대한 정답. 이것은 "IDisposable 객체를 더 쉽게 소비하는 방법"에 대한 훌륭한 해답이 될 것입니다.
John Saunders

10

IDisposable을 필요로하지 않아도 구현 해야하는지에 대한 질문에 아무도 대답하지 않았습니다.

짧은 답변 : 아니오

긴 대답 :

이것은 클래스의 소비자가 'using'을 사용할 수있게합니다. 내가 물어볼 질문은-왜 그렇게 할까? 대부분의 개발자는 반드시 알아야한다는 것을 알지 못하면 어떻게 '사용'을 사용하지 않을 것입니다. 어느 한 쪽

  • 그것들은 경험으로부터 그것들을 볼 수 있습니다 (예를 들어 소켓 클래스)
  • 문서화
  • 그들은 조심스럽고 클래스가 IDisposable을 구현한다는 것을 알 수 있습니다.

따라서 IDisposable을 구현함으로써 개발자에게 (적어도 일부는)이 클래스가 풀어야 할 것을 마무리한다고 알립니다. 그들은 '사용'을 사용하지만 사용이 불가능한 다른 경우가 있습니다 (객체의 범위가 로컬이 아닙니다). 다른 경우에는 객체의 수명에 대해 걱정해야합니다. 걱정할 것입니다. 그러나 이것은 필요하지 않습니다

Idisposable을 구현하여 사용할 수 있도록하지만 사용자가 지시하지 않으면 사용하지 않습니다.

그러니 하지마


1
개발자가 IDisposable을 구현하는 객체에서 사용 / 처리를 사용하지 않는 이유를 이해하지 못합니다 (프로그램이 종료 될 예정이 아닌 한).
adrianm

1
요점은 개발자가 모든 코드 경로에 배치하기 위해 모든 호출을 작성하여 참조하지 않는 것입니다. 예를 들어 사전에 인스턴스를 넣으면 사전에서 항목을 삭제할 때 dispose를 호출해야합니다. 이 경우에는 필요하지 않은 많은 번거 로움
물건을

3
@ PM100 재 : 불필요하게는 IDisposable을 구현 -에 대한 자세한 기사가 codeproject.com/KB/dotnet/idisposable.aspx 이 생각 할 수있는 몇 가지 드문 경우에 대해 설명 (매우 드문, 나는 확신합니다). 요컨대, 미래 또는 파생 객체에서 IDisposable의 필요성을 예측할 수있는 경우 일부 파생 객체가 필요한 "슬라이스"문제를 피하기 위해 기본 클래스에서 IDisposable을 "no-op"로 구현하는 것을 고려할 수 있습니다. 처분 및 다른 사람은하지 않습니다.
케빈 P. 라이스

4
  1. 관리되지 않는 리소스를 사용하는 다른 관리되는 개체를 사용하는 경우 해당 개체를 확정해야 할 책임은 없습니다. 객체에 대해 Dispose가 호출되면 해당 객체에 대해 Dispose를 호출해야합니다.

  2. 클래스가 부족한 리소스를 사용하지 않으면 클래스를 IDisposable로 구현하는 이유를 알 수 없습니다. 다음과 같은 경우에만 그렇게해야합니다.

    • 지금 당장은 아니고 곧 개체에 부족한 리소스가있을 것입니다. "우리는 여전히 개발 중이므로 완료하기 전에 여기에있을 것입니다." ")
    • 부족한 자원 사용
  3. 예, 코드를 사용하는 코드는 객체의 Dispose 메서드를 호출해야합니다. 그리고 네, 객체를 사용하는 코드는 using당신이 보여준대로 사용할 수 있습니다 .

  4. (2 다시?) WebClient는 관리되지 않는 리소스 또는 IDisposable을 구현하는 다른 관리되는 리소스를 사용합니다. 그러나 정확한 이유는 중요하지 않습니다. 중요한 것은 IDisposable을 구현한다는 것입니다. 따라서 WebClient가 다른 리소스를 전혀 사용하지 않는 것으로 밝혀 졌더라도 객체를 처리 할 때 객체를 폐기하여 해당 지식에 따라 행동해야합니다.


4

패턴을 처분하십시오 :

public abstract class DisposableObject : IDisposable
{
    public bool Disposed { get; private set;}      

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

    ~DisposableObject()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!Disposed)
        {
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
            Disposed = true;
        }
    }

    protected virtual void DisposeManagedResources() { }
    protected virtual void DisposeUnmanagedResources() { }
}

상속의 예 :

public class A : DisposableObject
{
    public Component components_a { get; set; }
    private IntPtr handle_a;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeManagedResources");
          components_a.Dispose();
          components_a = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeUnmanagedResources");
          CloseHandle(handle_a);
          handle_a = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

public class B : A
{
    public Component components_b { get; set; }
    private IntPtr handle_b;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeManagedResources");
          components_b.Dispose();
          components_b = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeUnmanagedResources");
          CloseHandle(handle_b);
          handle_b = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

4

다른 답변 의 일부 측면은 두 가지 이유로 약간 잘못되었습니다.

먼저,

using(NoGateway objNoGateway = new NoGateway())

실제로는 다음과 같습니다.

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

OutOfMemory 예외가 없으면 'new'연산자가 'null'을 반환해서는 안되므로 이것은 우스운 소리로 들릴 수 있습니다. 그러나 다음과 같은 경우를 고려하십시오. 1. IDisposable 리소스를 반환하는 FactoryClass를 호출하거나 2. 구현에 따라 IDisposable에서 상속되거나 상속되지 않을 수있는 유형이있는 경우 IDisposable 패턴이 잘못 구현 된 것을 보았습니다. 개발자가 IDisposable (bad, bad, bad)에서 상속하지 않고 Dispose () 메서드를 추가하는 많은 클라이언트의 경우 속성이나 메소드에서 IDisposable 리소스가 반환되는 경우도 있습니다 (나쁜, 나쁜, 나쁜-IDisposable 리소스를 포기하지 마십시오)

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

'as'연산자가 null (또는 리소스를 반환하는 속성 또는 메서드)을 반환하고 'using'블록의 코드가 'null'을 방지하면 null 개체에서 Dispose를 호출하려고 할 때 코드가 끊어지지 않습니다. '내장'null 검사

답장이 정확하지 않은 두 번째 이유는 다음과 같은 stmt 때문입니다.

GC가 최종 결정자를 내립니다

첫째, GC 자체뿐만 아니라 Finalization도 결정적이지 않습니다. CLR은 언제 종료자를 호출할지 결정합니다. 즉, 개발자 / 코드는 모른다. IDisposable 패턴이 올바르게 구현되어 있고 (위에 게시 한대로) GC.SuppressFinalize ()가 호출 된 경우 Finalizer가 호출되지 않습니다. 이것이 패턴을 올바르게 구현해야하는 큰 이유 중 하나입니다. 논리 프로세서 수에 관계없이 관리되는 프로세스 당 1 개의 Finalizer 스레드 만 있기 때문에 GC.SuppressFinalize ()를 호출하지 않고 Finalizer 스레드를 백업하거나 중단하여 성능을 쉽게 저하시킬 수 있습니다.

내 블로그에 올바른 Dispose 패턴 구현을 게시했습니다. Dispose 패턴 을 올바르게 구현하는 방법


2
당신은 반드시 작성에 대한인가 NoGateway = new NoGateway();NoGateway != null?
Cœur

1
이것이 stackoverflow.com/a/898856/3195477을 참조하고 있습니까? 지금 이름이 'Icey'에 의해 게시 아무 대답이 없다
UuDdLrLrSs

@DaveInCaz 맞습니다. 나는 어디에서나 'Icey'를 보지 못했지만 내 답변의 맥락은 위의 링크에서 제공 한 답변을 지향하는 것 같습니다. 어쩌면 그는 사용자 이름을 변경했을까요?
Dave Black

@DaveBlack 쿨, 감사합니다. 방금 텍스트를 편집했습니다.
UuDdLrLrSs

2

1) WebClient는 관리 유형이므로 종료자가 필요하지 않습니다. 최종 사용자는 NoGateway 클래스의 Dispose ()를 사용하지 않고 GC에서 수집하지 않은 기본 유형을 정리해야하는 경우에 필요합니다. 이 경우 사용자가 Dispose ()를 호출하지 않으면 NoGateway가 수행 된 직후에 포함 된 WebClient가 GC에 의해 삭제됩니다.

2) 간접적으로 그렇습니다, 그러나 당신은 그것에 대해 걱정할 필요가 없습니다. 코드는 표준 그대로 정확하며 사용자가 Dispose ()를 매우 쉽게 잊는 것을 막을 수 없습니다.


2

msdn의 패턴

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

1
using(NoGateway objNoGateway = new NoGateway())

에 해당

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

GC가 객체를 파괴하면 파이널 라이저가 호출됩니다. 이것은 당신이 당신의 방법을 떠날 때와 완전히 다른 시간에있을 수 있습니다. 사용 블록을 떠난 직후 IDisposable 처리가 호출됩니다. 따라서이 패턴은 일반적으로 더 이상 필요하지 않은 즉시 리소스를 해제하는 데 사용합니다.


1
객체를 파괴하는 GC에 대해서는 종료자가 호출되지 않습니다. "Finalize"가 재정의되면 GC 가 객체를 파괴 했을 때 객체는 최종화가 필요한 객체의 대기열에 배치되어 일시적으로 객체에 대한 강력한 참조를 생성하고 적어도 일시적으로 "생존"합니다.
supercat December

-5

내가 아는 바로는 Finalizer / Destructor를 사용하지 않는 것이 좋습니다.

public ~MyClass() {
  //dont use this
}

대부분의 경우, 언제 또는 언제 호출되는지 알지 못하기 때문입니다. 폐기 방법은 특히 사용하거나 직접 폐기하는 경우에 훨씬 좋습니다.

사용하는 것이 좋습니다. 사용해 :)


2
당신은 응답자의 링크에 따라야합니다. 예, 사용 / 삭제가 더 좋지만 Disposable 클래스는 두 가지를 모두 구현해야합니다.
Henk Holterman

흥미롭게도 Microsoft에서 읽은 모든 문서 (예 : 프레임 워크 디자인 지침)는 소멸자를 사용하지 마십시오. 항상 IDisposable을 사용하십시오.
Nic Wise

5
수업을 사용 하는 것과 수업을 쓰는 것을 구별 하고 다시 읽으십시오.
Henk Holterman

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