ArgumentNullException을 던지는 데 어떻게 도움이됩니까?


27

방법이 있다고 가정 해 봅시다.

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

나는 던지는 ArgumentNullException것이 "올바른" 것이라고 믿도록 훈련 되었지만 "객체 참조가 객체의 인스턴스로 설정되지 않았습니다"오류는 버그가 있음을 의미합니다.

왜?

참조를 캐싱하고 someObject나중에 사용하는 경우 전달 될 때 null을 확인하고 일찍 실패하는 것이 좋습니다. 그러나 다음 줄에서이를 참조하지 않는다면 왜 점검을해야합니까? 어떤 식 으로든 예외를 던질 것입니다.

편집 :

방금 나에게 ... 역 참조 된 null에 대한 두려움이 당신을 확인하지 않는 C ++과 같은 언어에서 온 것입니까 (즉, 메모리 위치 0 + 방법 오프셋에서 일부 방법을 실행하려고 시도합니까)?


예외를 명시 적으로 작성하면 문서에서 직접 확인할 수있는 오류 가능성이 있음을 확인하는 데 도움이됩니다.
zzzzBov

답변:


34

변수를 즉시 역 참조하는 경우 어느 쪽이든 토론 할 수 있지만 ArgumentNullException을 선호합니다.
무슨 일이 일어나고 있는지 훨씬 더 명확합니다. 예외에는 NullReferenceException과 달리 null 인 변수의 이름이 포함됩니다. 특히 API 레벨에서 ArgumentNullException은 일부 호출자가 메소드의 계약을 존중하지 않았으며 실패가 반드시 임의의 오류이거나 더 심각한 문제는 아니라고 분명히 밝힙니다 (아직도 여전히 그렇습니다). 일찍 실패해야합니다.

또한, 나중에 유지 보수 담당자는 어떻게해야합니까? 첫 번째 역 참조 전에 코드를 추가하는 경우 ArgumentNullException을 추가하거나 단순히 코드를 추가 한 다음 나중에 NullReferenceException을 발생시킬 수 있습니까?


또한, 이것이 비 공용 메서드이거나 비 공용 클래스의 공용 메서드라면 null 검사에 대한 정당한 이유가 없다고 생각하십니까?
Scott Whitlock

나는 일반적으로 이러한 검사를 개인 메소드에 추가하지 않지만 여전히 개인 클래스의 공개 메소드에 추가하여 일관성을 유지할 수 있습니다. 개인 메서드의 경우 인수가 일부 공개 호출 수준에서 더 일찍 유효성이 검증된다는 가정을하는 경향이 있습니다.
Matt H

54

ArgumentNullException을 던지는 것이 "올바른"것이지만 "객체 참조가 객체의 인스턴스로 설정되지 않았습니다"라는 오류가 있다고 생각하도록 훈련을 받았습니다. 왜?

내가 M(x)작성한 메소드 를 호출한다고 가정 해보십시오 . 나는 null을 전달합니다. 이름이 "x"로 설정된 ArgumentNullException이 발생합니다. 그 예외는 명백하게 내가 버그가 있음을 의미합니다. x에 null을 전달해서는 안됩니다.

내가 작성한 메소드 M을 호출한다고 가정 해보십시오. 나는 null을 전달합니다. null deref 예외가 발생합니다. 내가 버그를 가지고 있는지 또는 버그를 가지고 있는지 또는 제 대신에 제 3 자 라이브러리가 버그 가지고 있는지 여부를 어떻게 알 수 있습니까? 내 코드를 수정해야하는지 또는 전화로 수정해야하는지 알 수 없습니다.

두 예외는 모두 버그를 나타냅니다. 프로덕션 코드에서는 절대로 던져 지지 않아야 합니다. 문제는 디버깅을 수행하는 사람에게 버그를 더 잘 알려주는 예외 무엇입니까? 널 무시 예외는 거의 아무것도 알려주지 않습니다. 인수 null 예외는 많은 것을 알려줍니다. 사용자에게 친절하십시오. 그들이 실수로부터 배울 수있는 예외를 던지십시오.


"neither should ever be thrown in production code"어떤 다른 유형의 코드 / 상황을 사용 해야하는지 잘 모르겠습니다 . 그들은 같이 컴파일해야합니까 Debug.Assert? 아니면 API에서 버리지 않는다는 의미입니까?
StuperUser

6
@StuperUser : 혼란스러운 방식으로 썼기 때문에 내가 의미하는 바를 오해했다고 생각합니다. 당신은 해야throw new ArgumentNullException생산 코드에서, 그리고 그것은 테스트 케이스의 외부를 실행해서는 안됩니다 . 호출자가 그 버그를 가져서는 안되기 때문에 예외는 실제로 발생해서는 안됩니다.
Eric Lippert

1
버그가있는 사람과 통신 할뿐만 아니라 버그가 있는 곳 과 통신 합니다. 두 영역이 모두 내 경우에도 변수가 null 인 변수를 정확하게 이해하는 것이 항상 쉬운 것은 아닙니다.
하드 슈나이더

5

요점은 코드가 의미있는 일을해야한다는 것입니다.

A NullReferenceException는 모든 의미를 가질 수 있기 때문에 특별히 의미가 없습니다. 예외가 어디서 발생했는지 (그리고 코드를 사용할 수없는 경우) 보지 않으면 이해할 수 없었습니다.

A ArgumentNullException는 의미 someObjectnull있습니다. 인수가이므로 작업이 실패했습니다 . 나는 그것을 알아 내기 위해 코드를 볼 필요가 없습니다.

또한이 예외를 발생시키는 것은 명시 적으로 처리하는 것을 의미 null하지만 유일한 방법은 처리 할 수는 없지만 처리 할 수는 없지만 코드 충돌을 허용하면 실제로 null을 처리 할 수 ​​있음을 의미 할 수 있습니다 하지만 사례를 구현하는 것을 잊었습니다.

예외 사항은 실패시 정보를 전달하는 것인데, 런타임시 처리하여 실패에서 복구하거나 디버그 시간에 무엇이 잘못되었는지 더 잘 이해하도록 처리 할 수 ​​있습니다.


2

유일한 장점은 예외에서 더 나은 진단을 제공 할 수 있다는 것입니다. 즉, 함수의 호출자가 null을 전달하는 반면 역 참조가 throw하도록하면 호출자가 잘못된 데이터를 전달하는지 또는 함수 자체에 버그가 있는지 확실하지 않습니다.

누군가 다른 사람이 함수를 호출하여 사용하기 쉽도록 (문제가 발생하면 디버그) 내 코드에서 수행하지 않으면 런타임 코드가 어쨌든 확인하기 때문에 수행 할 것입니다.


1

ArgumentNullException을 던지는 것이 "올바른"것이지만 "객체 참조가 객체의 인스턴스로 설정되지 않았습니다"라는 오류가 있다고 생각하도록 훈련을 받았습니다.

코드가 설계된대로 작동하지 않으면 버그가있는 것입니다. 은 NullReferenceException시스템에 의해 발생되고 그 사실이 정말 더 많은 기능보다는 버그 있습니다. 처리 할 특정 예외를 정의하지 않았기 때문 만이 아닙니다. 또한 개발자가 코드를 읽는 경우 상황이 발생하지 않았다고 가정 할 수 있습니다.

비슷한 이유로 ApplicationException응용 프로그램에 Exception속하는 것과 운영 체제에 속하는 일반적인 것 입니다. 발생되는 예외 유형은 예외가 발생한 위치를 나타냅니다.

null을 고려한 경우 특정 예외를 처리하거나 입력이 허용 가능한 경우 예외를 처리하지 않으면 예외를 처리하지 않는 것이 좋습니다. 기본값이 양호하거나 전혀 작동하지 않는 작업을 수행하여 처리하십시오 (예 : 업데이트 필요 없음).

마지막으로, 상황을 처리하는 발신자의 능력을 무시하지 마십시오. 더 구체적인 예외를 ArgumentException주면 NullReferenceException생성자 변수 초기화 실패와 같이 다른 곳에서 발생할 수있는 다른 여러 가지 원인으로 인해 발생할 수있는 보다 일반적인 오류 이전에이 오류를 처리하는 방식으로 try / catch를 구성 할 수 있습니다.


1

검사는 진행 상황을보다 명확하게하지만 실제 문제는 다음과 같은 코드로 인해 발생합니다.

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

여기에는 호출 체인이 있으며 null 검사가 없으면 someOtherObject의 오류 만 표시되며 문제가 처음으로 드러난 위치는 아닙니다. 즉, 호출 스택을 해석하거나 해석하는 데 시간이 낭비됩니다.

일반적으로 이런 종류의 일과 일관성을 유지하고 항상 null 검사를 추가하는 것이 좋습니다.이 경우 null을 선택하여 사례별로 선택하는 것보다 누락 된 경우를 쉽게 찾을 수 있습니다 (null이 항상 유효하지 않습니다).


1

고려해야 할 또 다른 이유는 null 객체를 사용하기 전에 일부 처리가 발생하면 어떻게됩니까?

연결된 회사 ID (Cid) 및 개인 ID (Pid)의 데이터 파일이 있다고 가정합니다. 숫자 쌍의 스트림으로 저장됩니다.

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

이 VB 함수는 레코드를 파일에 씁니다.

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

경우 Person에 null 참조입니다, 선은 File.Write(Person.ID)예외가 발생합니다. 소프트웨어가 예외를 잡아서 복구 할 수 있지만 문제가됩니다. 회사 ID는 관련 개인 ID없이 작성되었습니다. 사실, 다음에이 함수를 호출하면 이전 사람 ID가 예상 된 곳에 회사 ID를 넣게됩니다. 그 기록이 부정확 할뿐만 아니라, 이후의 모든 기록에는 개인 신원과 회사 ID가 뒤따를 수 있습니다.

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

함수 시작시 매개 변수의 유효성을 검증하면 메소드의 실패가 보장 될 때 처리가 발생하지 않습니다.

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