C # 2008
나는이 문제에 대해 잠시 동안 노력해 왔으며 여전히 코드에서 finalize 및 dispose 메서드를 사용하는 것에 대해 혼란스러워합니다. 내 질문은 다음과 같습니다.
-
관리되지 않는 리소스를 처리하는 동안 종료자가 필요하다는 것을 알고 있습니다. 그러나 관리되지 않는 리소스를 호출하는 관리되는 리소스가있는 경우 여전히 종료자를 구현해야합니까?
-
그러나 관리되지 않는 리소스를 직접 또는 간접적으로 사용하지 않는 클래스를 개발하는 경우
IDisposable
해당 클래스의 클라이언트가 ‘using statement’를 사용할 수 있도록를 구현해야 합니까?클래스의 클라이언트가 using 문을 사용할 수 있도록 IDisposable을 구현하는 것이 가능합니까?
using(myClass objClass = new myClass()) { // Do stuff here }
-
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); } }
소스 코드에 대한 질문 :
-
여기서는 종료자를 추가하지 않았으며 일반적으로 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
-
WebClient
수업에서 수업을 사용하고NoGateway
있습니다. 인터페이스를WebClient
구현 하기 때문에 관리되지 않는 리소스IDisposable
를WebClient
간접적으로 사용 한다는 의미 입니까? 이것을 따라야하는 어렵고 빠른 규칙이 있습니까? 클래스가 관리되지 않는 리소스를 사용한다는 것을 어떻게 알 수 있습니까?
답변
권장되는 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는 나중에 종료자를 제외하더라도 종료 자 클래스를 다르게 처리하므로 종료자를 구현하지 않습니다. 또한 B
finalizer가 없지만 finalizer를 SuppressFinalize
구현하는 하위 클래스를 올바르게 처리하도록 호출 합니다.
클래스가 IDisposable 인터페이스를 구현할 때 클래스 사용이 끝나면 제거해야 할 관리되지 않는 리소스가있는 곳을 의미합니다. 실제 자원은 클래스 내에 캡슐화됩니다. 명시 적으로 삭제할 필요는 없습니다. Dispose()
클래스를 단순히 호출 하거나 래핑하면 using(...) {}
관리되지 않는 리소스가 필요에 따라 제거됩니다.
답변
구현할 공식 패턴 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
}
}
답변
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 );
}
}
이 구현을 내 블로그에 게시했습니다. 폐기 패턴을 올바르게 구현하는 방법
답변
pm100에 동의합니다 (이전 게시물에서이를 명시 적으로 말 했어야 함).
필요한 경우가 아니면 클래스에서 IDisposable을 구현해서는 안됩니다. 구체적으로 말하면 IDisposable을 구현해야 할 때 약 5 배가 있습니다.
-
클래스에는 IDisposable을 구현하고 클래스를 더 이상 사용하지 않으면 정리해야하는 관리 자원이 명시 적으로 포함됩니다 (상속을 통해 상속되지 않음). 예를 들어 클래스에 Stream, DbCommand, DataTable 등의 인스턴스가 포함 된 경우
-
클래스에는 Close () 메서드를 구현하는 관리 리소스 (예 : IDataReader, IDbConnection 등)가 명시 적으로 포함되어 있습니다. 이러한 클래스 중 일부는 Dispose () 및 Close () 메서드를 통해 IDisposable을 구현합니다.
-
클래스에는 명시 적으로 관리되지 않는 리소스 (예 : COM 개체, 포인터)가 포함됩니다 (예, 관리되는 C #에서 포인터를 사용할 수 있지만 ‘안전하지 않은’블록으로 선언해야합니다.) 관리되지 않는 리소스의 경우에는 RCW에서 System.Runtime.InteropServices.Marshal.ReleaseComObject ()를 호출합니다. 이론적으로 RCW는 관리되는 래퍼이지만 여전히 커버 아래에서 참조 카운트가 발생합니다.
-
클래스가 강력한 참조를 사용하여 이벤트를 구독하는 경우 이벤트에서 등록을 해제하거나 분리해야합니다. 등록 해제 / 분리를 시도하기 전에 항상 null이 아닌지 확인하십시오!.
-
수업에는 위의 조합이 포함되어 있습니다 …
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 ()’을 사용하지 마십시오
답변
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
이제 비공개입니다
답변
IDisposable을 필요로하지 않아도 구현 해야하는지에 대한 질문에 아무도 대답하지 않았습니다.
짧은 답변 : 아니오
긴 대답 :
이것은 클래스의 소비자가 ‘using’을 사용할 수있게합니다. 내가 물어볼 질문은-왜 그렇게 할까? 대부분의 개발자는 반드시 알아야한다는 것을 알지 못하면 어떻게 ‘사용’을 사용하지 않을 것입니다. 어느 한 쪽
- 그것들은 경험으로부터 그것들을 볼 수 있습니다 (예를 들어 소켓 클래스)
- 문서화
- 그들은 조심스럽고 클래스가 IDisposable을 구현한다는 것을 알 수 있습니다.
따라서 IDisposable을 구현함으로써 개발자에게 (적어도 일부는)이 클래스가 풀어야 할 것을 마무리한다고 알립니다. 그들은 ‘사용’을 사용하지만 사용이 불가능한 다른 경우가 있습니다 (객체의 범위가 로컬이 아닙니다). 다른 경우에는 객체의 수명에 대해 걱정해야합니다. 걱정할 것입니다. 그러나 이것은 필요하지 않습니다
Idisposable을 구현하여 사용할 수 있도록하지만 사용자가 지시하지 않으면 사용하지 않습니다.
그러니 하지마
답변
-
관리되지 않는 리소스를 사용하는 다른 관리되는 개체를 사용하는 경우 해당 개체를 확정해야 할 책임은 없습니다. 객체에 대해 Dispose가 호출되면 해당 객체에 대해 Dispose를 호출해야합니다.
-
클래스가 부족한 리소스를 사용하지 않으면 클래스를 IDisposable로 구현하는 이유를 알 수 없습니다. 다음과 같은 경우에만 그렇게해야합니다.
- 지금 당장은 아니고 곧 개체에 부족한 리소스가있을 것입니다. “우리는 여전히 개발 중이므로 완료하기 전에 여기에있을 것입니다.” “)
- 부족한 자원 사용
-
예, 코드를 사용하는 코드는 객체의 Dispose 메서드를 호출해야합니다. 그리고 네, 객체를 사용하는 코드는
using
당신이 보여준대로 사용할 수 있습니다 . -
(2 다시?) WebClient는 관리되지 않는 리소스 또는 IDisposable을 구현하는 다른 관리되는 리소스를 사용합니다. 그러나 정확한 이유는 중요하지 않습니다. 중요한 것은 IDisposable을 구현한다는 것입니다. 따라서 WebClient가 다른 리소스를 전혀 사용하지 않는 것으로 밝혀 졌더라도 객체를 처리 할 때 객체를 폐기하여 해당 지식에 따라 행동해야합니다.