development

C #에서 예외를 다시 발생시키는 올바른 방법은 무엇입니까?

big-blog 2020. 2. 18. 22:47
반응형

C #에서 예외를 다시 발생시키는 올바른 방법은 무엇입니까? [복제]


이 질문에는 이미 답변이 있습니다.

내 파트너와는 다른 방식으로 일을하면서 나오는 질문이 있습니다.

이것을하는 것이 낫습니다 :

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw;
}

아니면 이거:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw ex;
}

그들은 같은 일을합니까? 하나는 다른 것보다 낫습니까?


예외를 다시 발생 시키려면 항상 다음 구문을 사용해야합니다. 그렇지 않으면 스택 추적이 멈 춥니 다.

throw;

"throw ex"로 인한 추적을 인쇄하면 예외의 실제 소스가 아니라 해당 명령문으로 끝납니다.

기본적으로 "throw ex"를 사용하는 것은 범죄 행위로 간주됩니다.


내가 선호하는 것은

try 
{
}
catch (Exception ex)
{
     ...
     throw new Exception ("Put more context here", ex)
}

이렇게하면 원래 오류는 유지되지만 개체 ID, 연결 문자열 등의 컨텍스트를 더 많이 넣을 수 있습니다. 종종 내 예외보고 도구에는보고 할 5 개의 연결 예외가 있으며, 각보고는 더 자세하게보고됩니다.


당신이 예외가 발생하는 경우 에서 변수 (두 번째 예) 스택 트레이스는 원래의 방법을 포함한다 예외 던졌다.

첫 번째 예에서 StackTrace는 현재 방법을 반영하도록 변경됩니다.

예:

static string ReadAFile(string fileName) {
    string result = string.Empty;
    try {
        result = File.ReadAllLines(fileName);
    } catch(Exception ex) {
        throw ex; // This will show ReadAFile in the StackTrace
        throw;    // This will show ReadAllLines in the StackTrace
    }

첫 번째는 예외의 원래 스택 추적을 유지하고 두 번째는 예외를 현재 위치로 대체합니다.

따라서 첫 번째는 BY FAR이 더 낫습니다.


나는 이것이 오래된 질문이라는 것을 알고 있지만 여기의 모든 답변에 동의하지 않기 때문에 대답 할 것입니다.

이제 대부분의 경우 평범한 throw일을하고 싶거나 잘못된 일에 대해 가능한 많은 정보를 보존하거나 예외를 내부 예외로 포함 할 수있는 새 예외를 던지려 는 경우에 동의합니다. 그 원인이 된 내부 사건에 대해 알고 싶어하는 정도에 따라 다릅니다.

그러나 예외가 있습니다. 메소드가 다른 메소드를 호출하는 경우가 여러 가지 있으며 내부 호출에서 예외를 발생시키는 조건은 외부 호출에서 동일한 예외로 간주되어야합니다.

한 가지 예는 다른 컬렉션을 사용하여 구현 된 특수화 된 컬렉션입니다. a DistinctList<T>를 감싸고 List<T>있지만 중복 항목을 거부 한다고 가정 해 봅시다 .

누군가 ICollection<T>.CopyTo컬렉션 클래스를 호출 CopyTo한 경우 내부 컬렉션 을 직접 호출 할 수 있습니다 (예를 들어, 모든 사용자 지정 논리는 컬렉션에 추가하거나 설정하는 경우에만 적용됨). 이제 해당 호출이 발생하는 조건은의 문서와 일치하도록 컬렉션이 발생해야하는 조건과 정확히 동일합니다 ICollection<T>.CopyTo.

이제는 실행을 전혀 잡지 못하고 통과시킬 수 없었습니다. 여기에서 사용자가에서 List<T>무언가를 호출 할 때 예외가 발생합니다 DistinctList<T>. 세상의 끝이 아니라 구현 세부 사항을 숨기고 싶을 수도 있습니다.

또는 당신은 당신의 자신의 검사를 할 수 있습니다 :

public CopyTo(T[] array, int arrayIndex)
{
  if(array == null)
    throw new ArgumentNullException("array");
  if(arrayIndex < 0)
    throw new ArgumentOutOfRangeException("arrayIndex", "Array Index must be zero or greater.");
  if(Count > array.Length + arrayIndex)
    throw new ArgumentException("Not enough room in array to copy elements starting at index given.");
  _innerList.CopyTo(array, arrayIndex);
}

그것은 상용구이기 때문에 더 나쁜 코드는 아니며 아마도 CopyTo단순한 통과가 아닌 다른 구현에서 복사 하여 직접 구현해야 할 수도 있습니다. 여전히, 에서 수행 될 것과 동일한 검사를 불필요하게 반복 _innerList.CopyTo(array, arrayIndex)하므로 코드에 추가 된 유일한 것은 버그가있을 수있는 6 줄입니다.

우리는 확인하고 포장 할 수 있습니다 :

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentNullException ane)
  {
    throw new ArgumentNullException("array", ane);
  }
  catch(ArgumentOutOfRangeException aore)
  {
    throw new ArgumentOutOfRangeException("Array Index must be zero or greater.", aore);
  }
  catch(ArgumentException ae)
  {
    throw new ArgumentException("Not enough room in array to copy elements starting at index given.", ae);
  }
}

잠재적으로 버그가있을 수있는 새로운 코드가 추가되면 더 나빠질 수 있습니다. 그리고 우리는 내부 예외로부터 아무것도 얻지 못합니다. 이 메소드에 null 배열을 전달하고를 수신 ArgumentNullException하면 내부 예외를 검사하고에 대한 호출 _innerList.CopyTo이 null 배열을 전달하고를 던졌다는 것을 학습하여 아무것도 배우지 않습니다 ArgumentNullException.

여기에서 원하는 모든 것을 할 수 있습니다.

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentException ae)
  {
    throw ae;
  }
}

사용자가 잘못된 인수로 호출하면 예외가 발생하여 다시 던질 때 올바르게 throw됩니다. 여기에 사용 된 논리에 버그가있는 경우 두 줄 중 하나에 있습니다.이 접근법이 작동하는 경우를 결정하는 데 잘못되었거나 ArgumentException예외 유형을 찾는 데 잘못되었습니다 . catch 블록이 가질 수있는 유일한 버그는 두 가지입니다.

지금. 나는 여전히 대부분의 경우 평범한 throw;것을 원하거나 문제의 방법의 관점에서 문제와 더 직접적으로 일치하도록 자신의 예외를 구성하고 싶다는 데 동의합니다 . 위와 같이 다시 던지는 것이 더 합리적 인 경우가 있으며 다른 많은 경우가 있습니다. 원자 파일 리더가 구현하는 경우 예는 매우 다른 예를 데리고 FileStream와이 XmlTextReader후는 아마도 그 클래스에서받은 정확히 같은 예외를 throw 할 것이다, 파일 오류 또는 유효하지 않은 XML을 수신하지만, 호출자에게 보일 것 그것은 또는을 AtomFileReader던지고 있기 때문에, 그것들은 비슷하게 다시 던질 후보가 될 수 있습니다.FileNotFoundExceptionXmlException

편집하다:

두 가지를 결합 할 수도 있습니다.

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentException ae)
  {
    throw ae;
  }
  catch(Exception ex)
  {
    //we weren't expecting this, there must be a bug in our code that put
    //us into an invalid state, and subsequently let this exception happen.
    LogException(ex);
    throw;
  }
}

항상 "throw"를 사용해야합니다. .NET에서 예외를 다시 발생시키기 위해

이것을 참조하십시오 http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

기본적으로 MSIL (CIL)에는 "throw"및 "rethrow"와 C #의 "throw ex;"라는 두 가지 명령이 있습니다. MSIL의 "throw"및 C #의 "throw;"로 컴파일됩니다. -MSIL에 "다시 던져"! 기본적으로 "throw ex"가 스택 추적을 재정의하는 이유를 알 수 있습니다.


첫 번째가 더 좋습니다. 두 번째 디버그를 시도하고 호출 스택을 보면 원래 예외의 출처를 알 수 없습니다. 정말로 다시 던질 필요가 있다면 콜 스택을 그대로 유지하는 트릭이 있습니다 (검색을 시도하십시오. 이전에 대답했습니다).


예외가 발생하는 것과 동일한 방법으로 예외가 발생하면 스택 추적이 보존되지 않습니다.

void testExceptionHandling()
{
    try
    {
        throw new ArithmeticException("illegal expression");
    }
    catch (Exception ex)
    {
        throw;
    }
    finally
    {
        System.Diagnostics.Debug.WriteLine("finally called.");
    }
}

때에 따라 다르지. 디버그 빌드에서 가능한 적은 노력으로 원본 스택 추적을보고 싶습니다. 이 경우에는 "던져라" 청구서에 맞습니다. 그러나 릴리스 빌드에서 (a) 포함 된 원래 스택 추적으로 오류를 기록하고, 완료되면 (b) 오류 처리를 개선하여 사용자에게보다 합리적입니다. 여기서 "예외 발생"이 의미가 있습니다. 오류를 다시 던지면 원래 스택 추적이 삭제되지만 개발자가 아닌 사람도 스택 추적 정보를 볼 수 없으므로 오류를 다시 던질 수 있습니다.

        void TrySuspectMethod()
        {
            try
            {
                SuspectMethod();
            }
#if DEBUG
            catch
            {
                //Don't log error, let developer see 
                //original stack trace easily
                throw;
#else
            catch (Exception ex)
            {
                //Log error for developers and then 
                //throw a error with a user-oriented message
                throw new Exception(String.Format
                    ("Dear user, sorry but: {0}", ex.Message));
#endif
            }
        }

"Throw :"vs. "Throw ex;" 그것은 약간 청어를 만듭니다. 진정한 선택은 "Throw;"입니다. "예외를 던지십시오." "예외 던지기"의 특별한 경우는 아닙니다.

참고 URL : https://stackoverflow.com/questions/178456/what-is-the-proper-way-to-re-throw-an-exception-in-c



도와주세요.
반응형