문제

저는 C# 수업을 듣고 있습니다. Dispose 기능을 통해 IDisposable.내부에서 사용하도록 되어 있습니다. using 처리하는 값비싼 리소스를 즉시 해제할 수 있도록 차단합니다.

문제는 이전에 예외가 발생했을 때 버그가 발생했다는 것입니다. Dispose 호출되었고 프로그래머가 사용을 무시했습니다. using 또는 finally.

C++에서는 이에 대해 걱정할 필요가 없었습니다.클래스의 소멸자에 대한 호출은 객체 범위의 끝에 자동으로 삽입됩니다.이러한 일이 발생하지 않도록 하는 유일한 방법은 new 연산자를 사용하고 포인터 뒤에 개체를 유지하는 것입니다. 그러나 프로그래머에게 추가 작업이 필요한 것은 사용을 잊어버리는 등 실수로 수행할 작업이 아닙니다. using.

할 수 있는 방법이 있나요? using 블록이 C#에서 자동으로 사용됩니까?

많은 감사를 드립니다.

업데이트:

종료자 답변을 수락하지 않는 이유를 설명하고 싶습니다.이러한 답변은 기술적으로는 정확하지만 C++ 스타일 소멸자는 아닙니다.

내가 발견한 버그는 다음과 같습니다. 필수 항목으로 축소되었습니다.

try
{
    PleaseDisposeMe a = new PleaseDisposeMe();
    throw new Exception();
    a.Dispose();
}
catch (Exception ex)
{
    Log(ex);
}

// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();

사용 FXCop 훌륭한 제안이지만 그것이 나의 유일한 대답이라면 내 질문은 C# 사람들에게 호소가 되거나 C++를 사용해야 할 것입니다.20개의 중첩된 using 문이 있나요?

도움이 되었습니까?

해결책

불행히도 코드에서 직접 이를 수행할 수 있는 방법은 없습니다.이것이 사내 문제인 경우 이러한 종류의 문제를 포착할 수 있는 다양한 코드 분석 솔루션이 있습니다.FxCop을 살펴보셨나요?나는 이것이 이러한 상황과 IDisposable 개체가 매달린 상태로 남아 있을 수 있는 모든 경우를 포착할 것이라고 생각합니다.조직 외부에서 사람들이 사용하는 구성 요소이고 FxCop을 요구할 수 없는 경우 문서화는 실제로 유일한 수단입니다 :).

편집하다:종료자의 경우 종료가 언제 발생하는지 실제로 보장하지 않습니다.따라서 이것은 귀하에게 해결책이 될 수 있지만 상황에 따라 다릅니다.

다른 팁

제가 일하는 곳에서는 다음 지침을 사용합니다.

  • 각 IDisposable 클래스 ~ 해야 하다 종료자가 있습니다
  • IDisposable 개체를 사용할 때마다 "using" 블록 내에서 사용해야 합니다.유일한 예외는 개체가 다른 클래스의 멤버인 경우입니다. 이 경우 포함 클래스는 IDisposable이어야 하며 자체 'Dispose' 구현에서 멤버의 'Dispose' 메서드를 호출해야 합니다.즉, 다른 'Dispose' 메서드 내부를 제외하고는 개발자가 'Dispose'를 호출해서는 안 되며 질문에 설명된 버그를 제거해야 합니다.
  • 각 Finalizer의 코드는 Finalizer가 호출되었음을 알리는 경고/오류 로그로 시작해야 합니다.이렇게 하면 코드를 공개하기 전에 위에서 설명한 버그를 발견할 가능성이 매우 높으며 시스템에서 발생하는 버그에 대한 힌트가 될 수도 있습니다.

우리의 삶을 더 쉽게 만들기 위해 인프라에는 SafeDispose 메서드가 있습니다. 이 메서드는 만일의 경우에 대비해 try-catch 블록(오류 로깅 포함) 내에서 해당 인수의 Dispose 메서드를 호출합니다(Dispose 메서드는 예외를 발생시키지 않아야 하지만). ).

또한보십시오: 크리스 라이언IDisposable에 관한 님의 제안

편집하다:@투쟁:해야 할 한 가지는 'Dispose' 내에서 GC.SuppressFinalize를 호출하여 개체가 삭제된 경우 "재처리"되지 않도록 하는 것입니다.

또한 일반적으로 객체가 이미 폐기되었는지 여부를 나타내는 플래그를 보유하는 것이 좋습니다.다음 패턴은 일반적으로 매우 좋습니다.

class MyDisposable: IDisposable {
    public void Dispose() {
        lock(this) {
            if (disposed) {
                return;
            }

            disposed = true;
        }

        GC.SuppressFinalize(this);

        // Do actual disposing here ...
    }

    private bool disposed = false;
}

물론 잠금이 항상 필요한 것은 아니지만 클래스가 멀티 스레드 환경에서 사용될지 여부가 확실하지 않은 경우 잠금을 유지하는 것이 좋습니다.

@투쟁

개체가 범위 밖으로 이동되고 가비지 수집기에 의해 정리되면 If가 호출됩니다.

이 진술은 오해의 소지가 있으며 내가 읽은 방식이 잘못되었습니다.종료자가 언제 호출될지는 전혀 보장되지 않습니다.billpg가 종료자를 구현해야 한다는 당신의 주장은 절대적으로 옳습니다.그러나 객체가 원하는 대로 범위를 벗어나면 자동으로 호출되지 않습니다. 증거, 아래의 첫 번째 글머리 기호 마무리 작업에는 다음과 같은 제한 사항이 있습니다..

실제로 Microsoft는 Chris Sells에게 가비지 수집 대신 참조 계산을 사용하는 .NET 구현을 만들도록 권한을 부여했습니다. 링크.알고 보니 많은 실적 히트.

~ClassName()
{
}

편집(굵게):

개체가 범위 밖으로 이동되고 가비지 수집기에 의해 정리될 때 호출됩니다. 그러나 이는 결정적이지 않으며 특정 시간에 발생한다고 보장되지 않습니다..이것을 Finalizer라고 합니다.종료자가 있는 모든 개체는 종료 메서드가 호출되는 가비지 수집기에 의해 특수 종료 큐에 배치됩니다(따라서 기술적으로 빈 종료자를 선언하는 것은 성능 저하입니다).

관리되지 않는 리소스에 대한 프레임워크 지침에 따른 "허용된" 처리 패턴은 다음과 같습니다.

    public class DisposableFinalisableClass : IDisposable
    {
        ~DisposableFinalisableClass()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // tidy managed resources
            }

            // tidy unmanaged resources
        }
    }

따라서 위의 내용은 누군가 Dispose를 호출하면 관리되지 않는 리소스가 정리된다는 의미입니다.그러나 누군가가 Dispose 호출을 잊어버리거나 Dispose 호출을 방해하는 예외가 발생하는 경우 관리되지 않는 리소스는 여전히 정리됩니다. 약간 나중에 GC가 지저분한 작업을 수행할 때(애플리케이션이 종료되거나 예기치 않게 종료되는 경우 포함) ).

가장 좋은 방법은 수업에서 종료자를 사용하고 항상 다음을 사용하는 것입니다. using 블록.

하지만 실제로는 직접적인 상응하는 항목은 없습니다. 종료자는 C 소멸자와 비슷해 보이지만 다르게 동작합니다.

넌 둥지를 틀어야 해 using 블록이 있기 때문에 C# 코드 레이아웃에서는 기본적으로 블록을 같은 줄에 배치합니다.

using (SqlConnection con = new SqlConnection("DB con str") )
using (SqlCommand com = new SqlCommand( con, "sql query") )
{
    //now code is indented one level
    //technically we're nested twice
}

사용하지 않을 때는 using 어쨌든 내부적으로 수행되는 작업을 수행할 수 있습니다.

PleaseDisposeMe a;
try
{
    a = new PleaseDisposeMe();
    throw new Exception();
}
catch (Exception ex) { Log(ex); }  
finally {    
    //this always executes, even with the exception
    a.Dispose(); 
}

관리되는 코드를 사용하면 C#은 물건이 제대로 처리되지 않은 경우에도 자체 메모리를 관리하는 데 매우 능숙합니다.관리되지 않는 리소스를 많이 다루는 경우 그다지 강력하지 않습니다.

이는 프로그래머가 사용법을 잊어버린 것과 다르지 않습니다. 삭제 C++에서는 적어도 여기서는 가비지 수집기가 결국 따라잡을 것이라는 점만 제외하면 됩니다.

그리고 걱정되는 유일한 리소스가 메모리라면 IDisposable을 사용할 필요가 없습니다.프레임워크는 이를 자체적으로 처리합니다.IDisposable은 데이터베이스 연결, 파일 스트림, 소켓 등과 같은 관리되지 않는 리소스에만 사용됩니다.

더 나은 설계는 이 클래스가 값비싼 리소스를 폐기하기 전에 자체적으로 해제하도록 만드는 것입니다.

예를 들어 데이터베이스 연결인 경우 필요할 때만 연결하고 실제 클래스가 삭제되기 훨씬 전에 즉시 해제합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top