답변:
다른 사람들은 이미 ( Dispose
와 언어 정의에서 소멸 Finalize
자라고하는 Finalize
방법 사이의) 차이점을 이미 다루었 으므로 Finalize
메소드가 유용한 시나리오에 대해 조금만 추가 할 것입니다.
일부 유형은 일회용 자원을 사용하고 폐기하기 쉬운 방식으로 일회용 자원을 캡슐화합니다. 일반적인 사용법은 종종 다음과 같습니다 : 열기, 읽기 또는 쓰기, 닫기 (처리). using
구조 와 매우 잘 어울립니다 .
다른 사람들은 조금 더 어렵다. WaitEventHandles
예를 들어 하나의 스레드에서 다른 스레드로 신호를 보내는 데 사용되므로 이와 같이 사용되지 않습니다. 그러면 질문은 누가 Dispose
이것을 불러야 합니까? 이와 같은 보호 유형으로, Finalize
메소드가 구현 되어 인스턴스가 더 이상 애플리케이션에서 참조되지 않을 때 자원이 삭제되도록합니다.
Finalize
정당화 될 수있는 가장 큰 상황 은 자원을 유지하는 데 관심이있는 많은 객체가있을 때이지만 자원에 관심이없는 객체가 그것이 있는지 여부를 알 수있는 방법은 없습니다 마지막 하나. 이 경우, Finalize
아무도 오브젝트에 관심이 없을 때만 발생합니다. 느슨해 진 타이밍은 Finalize
파일 및 잠금과 같은 대체 불가능한 리소스에는 끔찍하지만, 대체 가능한 리소스에는 적합 할 수 있습니다.
finalizer 메소드는 객체가 가비지 수집 될 때 호출되며 이러한 상황이 언제 발생하는지 보장 할 수 없습니다 (강제화 할 수는 있지만 성능이 저하 될 수 있습니다).
Dispose
반면에 방법은 당신이 청소하고 귀하가 취득한 모든 자원 (관리되지 않는 데이터, 데이터베이스 연결, 파일 핸들 등) 코드가 이루어집니다 순간을 해제 할 수 있도록 클래스를 생성 한 코드에 의해 호출하기위한 것입니다 당신의 물건.
표준 방법은 구현하는 것입니다 IDisposable
그리고 Dispose
당신은 당신의 객체를 사용할 수 있도록 using
한 Statment. 와 같은 using(var foo = new MyObject()) { }
. 그리고 종료 Dispose
코드에서는 호출 코드가 처리하지 못한 경우를 대비하여 호출합니다.
Finalize는 가비지 수집기가 개체를 회수 할 때 호출되는 백스톱 방법입니다. Dispose는 GC가 개체에 반올림 될 때까지 무기한으로 유지하지 않고 더 이상 필요하지 않을 때 유용한 네이티브 리소스 (창 핸들, 데이터베이스 연결 등)를 해제하기 위해 응용 프로그램에서 호출하는 "결정적 정리"방법입니다.
개체의 사용자는 항상 Dispose를 사용합니다. 마무리는 GC를위한 것입니다.
클래스의 구현 자로서 폐기해야하는 관리 자원을 보유하면 Dispose를 구현합니다. 네이티브 리소스를 보유하고 있으면 Dispose와 Finalize를 모두 구현하고 네이티브 리소스를 해제하는 일반적인 메서드를 호출합니다. 이러한 관용구는 일반적으로 호출을 true로 처리하고 호출을 false로 마무리하는 개인 Dispose (bool disposing) 방법을 통해 결합됩니다. 이 메소드는 항상 기본 자원을 해제 한 다음 처리 매개 변수를 확인하고, 참이면 관리 자원을 처리하고 GC.SuppressFinalize를 호출합니다.
Dispose
좋고, 올바르게 구현하는 것은 일반적으로 쉽다. Finalize
악의적이며 올바르게 구현하는 것은 일반적으로 어렵습니다. 무엇보다도 GC는 객체에 대한 참조가 존재하는 한 객체의 아이덴티티가 "재활용"되지 않도록 보장하기 때문에 Disposable
일부 객체는 이미 정리되어있을 수 있습니다. 문제 없어요; Dispose
이미 호출 된 객체에 대한 참조 는 이미 호출 된 객체에 대한 참조로 유지됩니다 Dispose
.
Fred
파일 핸들 # 42를 소유하고 닫으면 시스템은 다른 엔티티에 부여 된 파일 핸들에 동일한 번호를 첨부 할 수 있습니다. 이 경우 파일 핸들 # 42는 Fred의 닫힌 파일이 아니라 해당 다른 엔티티에서 사용중인 파일을 나타냅니다. 위해 Fred
가까운 핸들 # 42하려고 다시 재앙이 될 것입니다. 관리되지 않는 개체 하나가 아직 릴리스되었는지 여부를 100 % 안정적으로 추적하는 것이 가능합니다. 여러 객체를 추적하는 것은 훨씬 어렵습니다.
마무리
protected
하지, public
또는 private
메소드가 직접 응용 프로그램의 코드에서 호출 할 수 없습니다 수 있도록 함과 동시에, 그것은를 호출 할 수 base.Finalize
방법을처분
IDisposable
자가있는 모든 유형에 구현Dispose
메소드를 호출 한 후 오브젝트를 사용할 수 없는지 확인하십시오 . 즉, Dispose
메소드가 호출 된 후 오브젝트를 사용하지 마십시오 .Dispose
모든 IDisposable
유형을 완료하면 전화하십시오 .Dispose
오차를 발생시키지 않고 여러 번 호출 할 수 있습니다.Dispose
메소드를 사용하여 GC.SuppressFinalize
메소드 내에서 최종 호출을 나중에 억제Dispose
메소드 내에서 예외를 던지지 마십시오폐기 / 최종 패턴
Dispose
하고 Finalize
관리되지 않는 리소스로 작업 할 때. Finalize
구현이 실행됩니다 및 자원이 여전히 객체는 개발자가 전화를 무시하면 쓰레기도 수집 될 때 발표 될 것이다 Dispose
명시 적 방법을.Finalize
방법과 방법 에서 관리되지 않는 리소스를 정리하십시오 Dispose
. 또한 Dispose
메서드에서 관리되지 않는 리소스를 멤버로 사용하여 해당 클래스 내부의 구성 요소로 사용하는 모든 .NET 개체에 대해 메서드를 호출하십시오 Dispose
.이 개체를 더 이상 사용하지 않으면 GC에서 Finalize를 호출합니다.
Dispose는이 클래스의 사용자가 모든 리소스를 해제하기 위해 호출 할 수있는 일반적인 방법입니다.
사용자가 Dispose 호출을 잊어 버렸고 클래스에 Finalize가 구현되어 있으면 GC가 호출되는지 확인합니다.
MCSD Certification Toolkit (시험 70-483) 문서 193에는 몇 가지 열쇠가 있습니다.
소멸자 ≈ (거의 같음)base.Finalize()
, 소멸자는 소멸자의 코드를 실행 한 다음 기본 클래스의 Finalize 메소드를 호출하는 Finalize 메소드의 대체 버전으로 변환됩니다. 그렇다면 GC에 의존하기 때문에 언제 호출 될지 알 수없는 완전히 비 결정적입니다.
클래스에 관리 자원이없고 관리되지 않는 자원이 없으면 클래스를 구현 IDisposable
하거나 소멸자가 없어야합니다 .
클래스에 managed resources 만있는 경우 구현해야 IDisposable
하지만 소멸자가 없어야합니다. 소멸자가 실행될 때 관리 객체가 여전히 존재하는지 확인할 수 없으므로 해당 Dispose()
메소드를 호출 할 수 없습니다 .
클래스에 관리되지 않는 리소스 만IDisposable
있는 경우 프로그램이 호출하지 않는 경우 소멸자 를 구현 해야합니다 Dispose()
.
Dispose()
메소드는 두 번 이상 실행하기에 안전해야합니다. 변수를 사용하여 이전에 실행되었는지 여부를 추적하여이를 달성 할 수 있습니다.
Dispose()
관리되는 리소스와 관리되지 않는 리소스를 모두 해제해야합니다 .
소멸자는 관리되지 않는 리소스 만 해제해야합니다 . 소멸자가 실행될 때 관리 대상 개체가 여전히 존재하는지 확인할 수 없으므로 Dispose 메서드를 호출 할 수 없습니다. 이는 표준 protected void Dispose(bool disposing)
관리 패턴 을 사용하여 얻을 수 있으며 여기서 관리 자원 만 해제 (처리)됩니다 disposing == true
.
리소스를 해제 한 후을 Dispose()
호출해야GC.SuppressFinalize
개체가 종료 큐를 건너 뛸 수 있습니다.
관리되지 않고 관리되는 리소스가있는 클래스에 대한 구현 예 :
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
}
}
}
시간의 99 %는 걱정할 필요가 없습니다. :) 그러나 개체에 관리되지 않는 리소스 (예 : 창 핸들, 파일 핸들)에 대한 참조가 있으면 관리되는 개체가 해당 리소스를 해제 할 수있는 방법을 제공해야합니다. Finalize는 리소스 해제를 암시 적으로 제어합니다. 가비지 수집기에서 호출합니다. Dispose는 리소스 릴리스를 명시 적으로 제어 할 수있는 방법이며 직접 호출 할 수 있습니다.
가비지 콜렉션 의 주제에 대해 훨씬 더 배울 것이 있지만 그것은 시작입니다.
종료자는 암시 적 정리를위한 것입니다. 클래스 / 클래스가 리소스를 관리 할 때마다 처리 해야합니다 . 그렇지 않으면 핸들 / 메모리 등이 누출 될 것입니다.
종료자를 올바르게 구현하는 것은 매우 어렵고 가능한 경우 피해야합니다. SafeHandle
클래스 (.Net v2.0 이상에서 사용 가능)는 이제 더 이상 종료자를 구현할 필요가 거의 없음을 의미합니다.
이 IDisposable
인터페이스는 명시 적 정리를위한 것이며 훨씬 일반적으로 사용됩니다. 사용자가 객체 사용을 마칠 때마다 명시 적으로 리소스를 해제하거나 정리할 수 있도록하려면이 인터페이스를 사용해야합니다.
종료자가 IDisposable
있는 경우 개체를 가비지 수집 한 경우보다 더 빨리 해당 리소스를 명시 적으로 해제 할 수 있도록 인터페이스를 구현해야합니다 .
파이널 라이저 및에 대한 최상의 권장 사항으로 간주되는 내용은 DG 업데이트 : 폐기, 마무리 및 리소스 관리 를 참조하십시오 IDisposable
.
요약은-
또한 Dispose () 구현에서 또 다른 차이점은 관리되는 리소스도 해제 해야하지만 Finalizer에서는 수행하지 않아야합니다. 오브젝트가 참조하는 관리 자원이 완료되기 전에 이미 정리되었을 가능성이 높기 때문입니다.
관리되지 않는 리소스를 사용하는 클래스의 경우 모범 사례는 Dispose () 메서드와 Finalizer를 모두 정의하여 개발자가 개체를 명시 적으로 폐기하는 것을 잊어 버린 경우 대비책으로 사용하는 것입니다. 둘 다 공유 방법을 사용하여 관리되는 리소스와 관리되지 않는 리소스를 정리할 수 있습니다.
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
내가 아는 가장 좋은 예.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
C #에서 Finalize와 Dispose 메서드의 차이점
GC는 finalize 메소드를 호출하여 관리되지 않는 리소스 (예 : 파일 작업, Windows API, 네트워크 연결, 데이터베이스 연결)를 회수하지만 GC가 호출 할 때 시간이 고정되지 않습니다. GC에 의해 암시 적으로 호출됩니다. 이는 낮은 수준의 제어 권한이 없음을 의미합니다.
Dispose Method : 코드에서 호출 할 때 제어 수준이 낮습니다. 사용할 수 없다고 생각 될 때마다 관리되지 않는 리소스를 회수 할 수 있습니다. IDisposal 패턴을 구현하여이를 달성 할 수 있습니다.
클래스 인스턴스는 종종 창 핸들 (HWND), 데이터베이스 연결 등과 같이 런타임에 의해 관리되지 않는 리소스에 대한 제어를 캡슐화합니다. 따라서 이러한 자원을 해제 할 수있는 명시 적 및 암시 적 방법을 모두 제공해야합니다. 개체 (C #의 소멸자 구문 및 C ++의 Managed Extensions)에서 보호 된 Finalize 메서드를 구현하여 암시적인 제어 기능을 제공합니다. 가비지 수집기는 개체에 대한 유효한 참조가 더 이상 존재하지 않는 시점에서이 메서드를 호출합니다. 경우에 따라 가비지 수집기가 개체를 해제하기 전에 개체를 사용하여 프로그래머에게 이러한 외부 리소스를 명시 적으로 해제 할 수있는 기능을 제공 할 수 있습니다. 외부 리소스가 부족하거나 비싸면 더 이상 사용하지 않을 때 프로그래머가 명시 적으로 리소스를 해제하면 성능이 향상 될 수 있습니다. 명시적인 제어를 제공하려면 IDisposable 인터페이스에서 제공하는 Dispose 메서드를 구현하십시오. 객체의 소비자는 객체를 사용하여 완료되면이 메소드를 호출해야합니다. 객체에 대한 다른 참조가 살아 있어도 Dispose를 호출 할 수 있습니다.
Dispose를 통해 명시 적으로 제어 할 수있는 경우에도 Finalize 메서드를 사용하여 암시 적 정리를 제공해야합니다. Finalize는 프로그래머가 Dispose를 호출하지 않은 경우 리소스가 영구적으로 누출되지 않도록 백업을 제공합니다.
Dispose와 Finalize의 주요 차이점은 다음과 같습니다.
Dispose
일반적으로 코드에서 호출합니다. 리소스를 호출하면 즉시 해제됩니다. 사람들은 메소드를 호출하는 것을 잊어 버리므로 using() {}
진술이 발명됩니다. 프로그램이에서 코드 실행을 마치면 메소드가 자동으로 {}
호출 Dispose
됩니다.
Finalize
코드에서 호출하지 않습니다. 가비지 콜렉터 (GC)가 호출해야합니다. 즉, GC가 결정할 때마다 언제든지 리소스가 해제 될 수 있습니다. GC가 작업을 수행하면 많은 Finalize 메서드를 거치게됩니다. 이것에 논리가 많으면 프로세스 속도가 느려집니다. 프로그램 성능 문제가 발생할 수 있습니다. 당신이 거기에 넣는 것에주의하십시오.
나는 개인적으로 Dispose에서 대부분의 파괴 논리를 쓸 것입니다. 바라건대, 이것은 혼란을 해결합니다.
우리가 알다시피 dispose와 finalize는 관리되지 않는 리소스를 해제하는 데 사용됩니다. 그러나 차이점은 finalize는 두주기를 사용하여 리소스를 해제하는 반면, dispose는 하나의주기를 사용합니다.
첫 번째 부분에 대답하려면 사람들이 동일한 클래스 객체에 대해 다른 접근 방식을 사용하는 예제를 제공해야합니다. 그렇지 않으면 대답하기가 어렵거나 심지어 이상합니다.
두 번째 질문에 대해서는 먼저 IDisposable 인터페이스 를 올바르게 사용하는 것이 좋습니다.
당신의 선택입니다! 그러나 폐기를 선택하십시오.
즉, GC는 종료 자 (있는 경우. Microsoft의 소멸자라고도 함)에 대해서만 알고 있습니다. 좋은 코드는 마무리 및 처리에서 정리를 시도합니다.