C #에서 '널 던지기'를 허용하는 이유는 무엇입니까?


85

특히 복잡한 예외 처리 코드를 작성하는 동안 누군가가 물었습니다. 예외 개체가 null이 아닌지 확인할 필요가 없습니까? 그리고 나는 물론 아니라고 말했지만 시도하기로 결정했습니다. 분명히 null을 던질 수 있지만 여전히 어딘가에서 예외로 변합니다.

왜 이것이 허용됩니까?

throw null;

이 스 니펫에서 고맙게도 'ex'는 null이 아니지만 그럴 수 있습니까?

try
{
  throw null;
}
catch (Exception ex)
{
  //can ex ever be null?

  //thankfully, it isn't null, but is
  //ex is System.NullReferenceException
}

8
자신의 throw 위에 .NET 예외 (null 참조)가 throw되지 않는 것이 확실합니까?
micahtan 2010

4
컴파일러가 직접 "널을 던지려고"하는 것에 대한 경고를 적어도 발생시킬 것이라고 생각할 것입니다.
Andy White

아니요, 잘 모르겠습니다. 프레임 워크는 내 개체로 무언가를 시도 할 수 있으며 null 일 때 프레임 워크는 "Null Reference Exception"..을 발생시킵니다.하지만 궁극적으로 "ex"가 null이 될 수 없음을 확인하고 싶습니다
quip

1
@Andy : 경고는 언어의 다른 상황에서 의미가있을 수 있지만 , 처음에 예외를 던지는 것을 목적으로throw 하는 명령문의 경우 경고가 많은 가치를 추가하지 않습니다.
Mehrdad Afshari

@Mehrdad-예,하지만 개발자가 의도적으로 "null을 던지거나"NullReferenceException을보고 싶은지 의심됩니다. if 문에서 잘못된 = 비교와 같은 전체 빌드 오류 일 수 있습니다.
Andy White

답변:


96

언어 사양은 유형의 표현식을 예상하고 System.Exception(따라서 null해당 컨텍스트에서 유효 함)이 표현식을 널이 아닌 것으로 제한하지 않기 때문입니다. 일반적으로 해당 표현식의 값이 맞는지 여부를 감지 할 수있는 방법은 null없습니다. 중지 문제를 해결해야합니다. null어쨌든 런타임은 케이스 를 처리해야 합니다. 보다:

Exception ex = null;
if (conditionThatDependsOnSomeInput) 
    ex = new Exception();
throw ex; 

물론 그들은 null리터럴을 무효화 하는 특정한 경우를 만들 수는 있지만 그다지 도움이되지 않을 것입니다. 그렇다면 왜 사양 공간을 낭비하고 적은 이익을 위해 일관성을 감소시킬까요?

면책 조항 (Eric Lippert에게 맞기 전) : 이것은 이 디자인 결정의이면에있는 이유에 대한 저의 추측입니다. 물론 저는 디자인 회의에 참석하지 않았습니다.)


두 번째 질문에 대한 답은 catch 절 내에서 잡힌 식 변수가 null 일 수 있는지 여부입니다. C # 사양은 다른 언어로 인해 null예외가 전파 될 수 있는지 여부에 대해 침묵하지만 예외가 전파되는 방식을 정의합니다.

catch 절 (있는 경우)은 예외에 대한 적절한 핸들러를 찾기 위해 나타나는 순서대로 검사됩니다. 예외 유형 또는 예외 유형의 기본 유형을 지정하는 첫 번째 catch 절 은 일치로 간주됩니다. 일반 catch 절은 모든 예외 유형에 대한 일치로 간주됩니다. [...]

의 경우 null굵은 글씨는 거짓입니다. 따라서 순전히 C # 사양이 말하는 내용을 기반으로하지만 기본 런타임이 null을 throw하지 않는다고 말할 수는 없지만이 경우에도 일반 catch {}절에 의해서만 처리된다는 것을 확신 할 수 있습니다 .

CLI에서 C # 구현의 경우 ECMA 335 사양을 참조 할 수 있습니다. 이 문서는 CLI가 내부적으로 던지는 모든 예외를 정의하고 (아무것도 아님 null) 사용자 정의 예외 객체가 throw명령어에 의해 throw된다는 것을 언급합니다 . 해당 명령어에 대한 설명은 C # throw문과 거의 동일 합니다 (객체 유형을로 제한하지 않는다는 점 제외 System.Exception).

기술:

throw명령어는 O스택 에서 예외 객체 (type )를 throw하고 스택을 비 웁니다. 예외 메커니즘에 대한 자세한 내용은 파티션 I을 참조하십시오.
[참고 : CLI는 모든 개체의 throw를 허용하지만 CLS는 언어 상호 운용성을 위해 사용해야하는 특정 예외 클래스를 설명합니다. 끝 참고]

예외 :

System.NullReferenceException경우에 발생합니다 obj입니다 null.

단정:

올바른 CIL은 객체가 항상 null또는 객체 참조 (즉, 유형 O)인지 확인합니다.

나는 이것들이 잡힌 예외가 결코 없다는 결론을 내리기에 충분하다고 믿는다 null.


나는 그것이 할 수있는 최선의 방법은 throw null;.
FrustratedWithFormsDesigner

7
실제로 System.Exception에서 상속되지 않는 개체가 throw되는 것은 CLR에서 전적으로 가능합니다. 내가 아는 한 C #에서는 할 수 없지만 IL, C ++ / CLR 등을 통해 할 수 있습니다. 자세한 내용은 msdn.microsoft.com/en-us/library/ms404228.aspx 를 참조하십시오.
technophile

@technophile : 네. 나는 여기서 언어에 대해 이야기하고 있습니다. C #에서는 다른 유형의 예외를 throw 할 수 없지만 제네릭 catch { }절을 사용하여 예외를 포착 할 수 있습니다 .
Mehrdad Afshari

1
컴파일러 throw가 인수가 null 값일 수있는 인수 를 거부하는 것은 이치에 맞지 않지만 이것이 throw null합법적이어야 함을 의미하지는 않습니다 . 컴파일러는 throw인수에 식별 가능한 클래스 유형이 있다고 주장 할 수 있습니다. 다음과 같은 표현식 (System.InvalidOperationException)null은 컴파일 타임에 유효해야 NullReferenceException하지만 (실행하면 a가 발생 null해야 함) 유형 이 지정되지 않은 것이 허용되어야 한다는 의미는 아닙니다 .
supercat

@supercat : 사실이지만이 경우 null은 암시 적으로 Exception으로 형식화됩니다 (예외가 필요한 컨텍스트에서 사용되기 때문에). null은 런타임에 유효하지 않으므로 NullReferenceException이 throw됩니다 (Anon.의 답변에 언급 됨). throw 된 예외는 'throw'문에있는 예외가 아닙니다.
John B. Lambe

29

분명히 null을 던질 수 있지만 여전히 어딘가에서 예외로 변합니다.

null개체 를 throw하려고 하면 (완전히 관련이없는) Null 참조 예외가 발생합니다.

던질 수있는 null이유를 묻는 것은 왜 이렇게 할 수 있는지 묻는 것과 같습니다.

object o = null;
o.ToString();

5

여기 에서 가져온 :

C # 코드에서이 식을 사용하면 NullReferenceException이 발생합니다. 이는 throw- 문이 단일 매개 변수로 Exception 유형의 오브젝트를 필요로하기 때문입니다. 그러나이 객체는 내 예에서 null입니다.


5

던지기가 그것을 감지하고 NullReferenceException으로 바꿀 것이기 때문에 C #에서 null을 던지는 것이 불가능할 수도 있지만, null을받을 수 있습니다. 'ex'가 null이 될 것으로 예상) null 참조 예외가 발생하여 내 앱이 종료됩니다 (마지막 캐치이기 때문에).

따라서 C #에서 null을 throw 할 수는 없지만 netherworld는 null을 throw 할 수 있으므로 가장 바깥 쪽 catch (Exception ex)가이를 수신 할 준비를하는 것이 좋습니다. 참고로.


3
흥미 롭군. 코드를 게시하거나이 작업을 수행하는 깊이있는 부분을 피할 수 있습니까?
Peter Lillevold

CLR 버그인지는 말할 수 없습니다. .NET의 내장에서 null을 던진 내용과 호출 스택 또는 다른 단서가 포함 된 예외가 발생하지 않는 이유를 추적하는 것은 어렵습니다. 내 가장 바깥 쪽 캐치가 이제 사용하기 전에 null Exception 인수를 확인한다는 것을 알고 있습니다.
Brian Kennedy

3
CLR 버그이거나 검증 할 수없는 잘못된 코드 때문이라고 생각합니다. 올바른 CIL은 null이 아닌 개체 또는 null (NullReferenceException이 됨) 만 throw합니다.
Demi

또한 null 예외를 반환하는 서비스를 사용하고 있습니다. 널 예외가 어떻게 발생하는지 알아 내셨습니까?
themiDdlest 2011

2

나는 당신이 할 수 없다고 생각합니다-null을 던지려고 할 때 할 수 없으므로 null 참조 예외를 던지는 오류 경우에해야 할 일을합니다. 따라서 실제로 null을 던지는 것이 아니라 null을 던지지 않아 결과가 발생합니다.


2

".. 고맙게도 'ex'는 null이 아니지만 그럴 수 있습니까?":

우리는 틀림없이 null 인 예외를 던질 수 없기 때문에 catch 절도 null 인 예외를 포착 할 필요가 없습니다. 따라서 ex는 null이 될 수 없습니다.

지금이 질문은 사실 이미 된 것을 볼 수 물었다 .


@Brian Kennedy는 위의 주석에서 우리가 null을 잡을 수 있다고 말합니다.
ProfK

@ Tvde1-여기서 차이점은 네, 실행할 수 있다는 것 throw null입니다. 그러나 그것은 null 인 예외를 던지지 않을 것 입니다. 오히려 throw nullnull 참조에서 메서드를 호출하려고하기 때문에 이후에 런타임에서 null이 아닌 인스턴스를 throw하도록합니다 NullReferenceException.
Peter Lillevold

1

예외에는 예외가 발생한 위치에 대한 세부 정보가 포함되어 있습니다. 생성자가 던질 위치를 알지 못하기 때문에 throw 메서드가 던지는 지점에서 해당 세부 정보를 객체에 주입하는 것이 합리적입니다. 즉, CLR이 NullReferenceException을 트리거하는 null에 데이터를 주입하려고합니다.

이것이 정확히 무슨 일이 일어나고 있는지 확실하지 않지만 현상을 설명합니다.

이것이 사실이라고 가정하면 (그리고 null을 던지는 것보다 ex를 null로 만드는 더 좋은 방법을 생각할 수 없습니다), 그것은 ex가 null이 될 수 없다는 것을 의미합니다.


0

이전 C #에서 :

다음 구문을 고려하십시오.

public void Add<T> ( T item ) => throw (hashSet.Add ( item ) ? null : new Exception ( "The item already exists" ));

이보다 훨씬 짧다고 생각합니다.

public void Add<T> ( T item )
{
    if (!hashSet.Add ( item ))
        throw new Exception ( "The item already exists" );
}

이것은 우리에게 무엇을 말합니까?
bornfromanegg

덧붙여서, 당신의 짧은 정의가 무엇인지 잘 모르겠지만 두 번째 예에는 더 적은 문자가 포함되어 있습니다.
bornfromanegg

@bornfromanegg no the 1st is inlined, 그래서 확실히 더 짧습니다. 내가 말하려는 것은 조건에 따라 오류가 발생할 수 있다는 것입니다. 전통적으로 u는 두 번째 예제를 좋아하지만 "throw null"은 아무것도 던지지 않기 때문에 두 번째 예제를 인라인하여 첫 번째 예제처럼 보이게 할 수 있습니다. 또한 첫 번째 예는 두 번째보다 8 자 미만입니다
Arutyun Enfendzhyan

"throw null"은 NullReference 예외를 발생시킵니다. 즉, 첫 번째 예제는 항상 예외를 throw합니다.
bornfromanegg

예, 불행히도 지금은 그렇습니다. 하지만 전에는하지 않았습니다
Arutyun Enfendzhyan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.