null을 기대하지 않으면 null을 검사해야합니까?


113

지난주에 우리는 애플리케이션의 서비스 계층에서 널 처리에 대해 논란의 여지가있었습니다. 질문은 .NET 컨텍스트에 있지만 Java 및 다른 많은 기술에서도 동일합니다.

문제는 : 항상 null을 확인하고 코드가 무엇이든 상관없이 코드를 작동 시키거나 null이 예기치 않게 수신되면 예외가 발생하도록해야합니까?

한쪽에서, null을 확인하지 않으려는 경우 (즉, 처리 할 사용자 인터페이스가 없음) 빈 캐치가있는 try 블록을 작성하는 것과 같습니다. 오류를 숨기고 있습니다. 오류는 코드에서 무언가가 변경되어 null이 이제 예상 값이거나 다른 오류가 있고 잘못된 ID가 메소드에 전달되었을 수 있습니다.

반면, null 확인은 일반적으로 좋은 습관 일 수 있습니다. 또한 검사가 있으면 기능의 일부만 영향을 미치지 않으면 서 응용 프로그램이 계속 작동 할 수 있습니다. 그런 다음 고객은 "페이지 X를 열 수 없음"과 같은 훨씬 더 심각한 버그 대신 "댓글을 삭제할 수 없습니다"와 같은 작은 버그를보고 할 수 있습니다.

어떤 관행을 따르고 있으며 두 가지 접근법에 대한 또는 반대의 주장은 무엇입니까?

최신 정보:

특정 사례에 대한 세부 정보를 추가하고 싶습니다. 우리는 데이터베이스에서 일부 객체를 검색하고 일부 처리를 수행했습니다 (예를 들어 컬렉션을 작성하십시오). 코드를 작성한 개발자는 오브젝트가 널일 수 있다고 예상하지 않았으므로 점검을 포함하지 않았으며 페이지가로드 될 때 오류가 발생하여 전체 페이지가로드되지 않았습니다.

분명히이 경우에는 점검이 필요했습니다. 그런 다음 처리되지 않은 것으로 예상되는 경우에도 처리되는 모든 개체를 검사해야하는지 여부와 최종 처리를 자동으로 중단해야하는지에 대한 논쟁을 벌였습니다.

가상의 이점은 페이지가 계속 작동한다는 것입니다. 서로 다른 그룹 (사용자, 의견, 질문)으로 된 Stack Exchange의 검색 결과를 생각해보십시오. 이 메소드는 널 (null)을 점검하고 사용자 처리를 중단 할 수 있지만 (버그로 인해 널임) "주석"및 "질문"섹션을 리턴합니다. "사용자"섹션이없는 것을 제외하고는 페이지가 계속 작동합니다 (버그). 조기에 실패하여 전체 페이지를 깨거나 계속 작동하여 누군가 "사용자"섹션이 누락되었음을 알릴 때까지 기다려야합니까?


62
폭스 멀더 원칙 : 아무도 믿지 마십시오.
yannis

14
@YannisRizos : 그 원칙은 좋은 것입니다. Java와 C #을 만든 사람들이 언어 런타임 환경에서이를 자동으로 수행 할 수 있도록하는 것이 좋습니다. 따라서 일반적으로 해당 언어로 된 코드의 모든 곳에서 null을 명시 적으로 검사 할 필요가 없습니다 .
Doc Brown


나는 tdammers 답변에 동의합니다. null을 처리하는 합리적인 방법이 없기 때문에 null을 확인하는 것이 잘못이라고 생각합니다. 그러나 시나리오에서 사용자 유효성 검사 (또는 의견 수렴 등)가 실패하고 "오 오류가 발생하지 않았습니다"상자가있는 것과 같은 섹션에서 오류를 처리 할 수 ​​있습니다. 나는 개인적으로 내 응용 프로그램에있는 모든 예외를 기록하고 가까운 예기치 않게 404 및 연결과 같은 특정 예외를 필터링

2
assert(foo != null, "foo is web control within the repeater, there's no reason to expect it to be null, etc, etc...");
zzzzBov

답변:


105

null런타임에서 예외를 확인해야하는지 아니면 예외를 발생 시켜야하는지에 대한 질문은 그리 많지 않습니다 . 그러한 예기치 않은 상황에 대응하는 방법입니다.

따라서 귀하의 옵션은 다음과 같습니다.

  • 일반적인 예외 ( NullReferenceException)를 던지고 거품을 일으킨다. null직접 확인 하지 않으면 자동으로 발생합니다.
  • 문제를 더 높은 수준에서 설명하는 사용자 지정 예외를 처리하십시오. 이것은 null수표를 NullReferenceException던지거나 더 구체적인 예외 를 잡아서 던질 수 있습니다 .
  • 적절한 기본값을 대체하여 복구하십시오.

어느 것이 가장 좋은 솔루션인지에 대한 일반적인 규칙은 없습니다. 개인적으로 다음과 같이 말합니다.

  • 오류 조건이 코드에서 심각한 버그의 징후 인 경우, 즉 null 값은 처음에 도착하지 않아야하는 경우 일반적인 예외가 가장 좋습니다. 이러한 예외는 사용자가 설정 한 오류 로깅에 영향을 줄 수 있으므로 누군가 알림을 받고 버그를 수정합니다.
  • 기술적으로 잘못된 HTML을 허용하고 현명하게 렌더링하기 위해 최선을 다하는 웹 브라우저와 같이 반 유용한 출력을 제공하는 것이 정확성보다 중요한 경우 기본값 솔루션이 좋습니다.
  • 그렇지 않으면 특정 예외로 가서 적절한 곳에서 처리합니다.

1
최종 사용자를위한 @Stilgar는 차이가 없습니다. 개발자에게 "데이터베이스 서버에 접근 할 수 없음"과 같은 특정 예외는 "널 포인터 제외 (null pointer excepteion)"보다 더 직관적입니다.
k3b

32
하드 및 조기 실패는 사물이 잘못 미묘하게 잘못 될 가능성, 인증 오류, 스토리지에 손상된 데이터 지속, 정보 유출 및 기타 재미있는 것들과 같은 것들이 적다는 것을 의미합니다.
Lars Viklund

1
@Stilgar : 여기에는 무인 행동과 유지 보수성이라는 두 가지 문제가 있습니다. 무인 동작의 경우 특정 예외와 일반 예외 간에는 거의 차이가 없지만 유지 관리의 경우 "아, 그래서 일이 터져 나가는 이유"와 여러 시간의 디버 슬론간에 차이를 만들 수 있습니다.
tdammers

3
tdammers가 옳습니다. "객체 참조가 객체의 인스턴스로 설정되지 않았습니다"는 가장 빈번하게 볼 수있는 가장 성가신 예외 중 하나입니다. 매개 변수 이름을 가진 ArgumentNullException인지 또는 사용자 TK421 사용자에 대한 포스트 레코드가 없습니다.
Sean Chase

1
@ k3b 최종 사용자에게도 "데이터베이스 서버에 접근 할 수 없음"이 매우 유용합니다. 적어도 일반적인 문제에 대한 단서가있는 최종 사용자의 경우 버그가있는 소프트웨어에 대해 화를 내기보다는 케이블이 느슨하거나 라우터를 다시 시작해야한다는 사실을 발견하는 데 도움이 될 수 있습니다.
Julia Hayward

58

IMHO가 예상하지 않은 null 값을 처리하려고하면 지나치게 복잡한 코드가 생성됩니다. null을 기대하지 않으면을 던져 명확하게하십시오 ArgumentNullException. 사람들이 값이 null인지 확인한 다음 이해가되지 않는 코드를 작성하려고하면 정말 실망 스럽습니다. SingleOrDefault누군가가 실제로 기대할 때 사용하고 (또는 더 나쁜 수집 을 사용하는 Single경우), 사람들이 논리를 명확하게 밝히기를 두려워하는 경우 (실제로 무엇을 알지 못하는 경우 )에도 마찬가지 입니다.


대신 ArgumentNullException코드 계약을 지원하지 않는 2010 년 이전의 소스 코드가 아닌 경우 코드 계약을 사용해야합니다.
Arseni Mourzenko

동의합니다. 널 (null)이 예상되지 않으면 이상한 방식으로 처리되지 말고보고되어야합니다.
조르지오

9
질문을 올바르게 읽으면 ArgumentNullExceptionnull 값을 "처리" 하는 것으로 카운트를 던집니다 . 나중에 null 참조 예외를 방지 합니다.
KutuluMike 2016 년

@ MichaelEdenfield : 주어진 질문에 따라 실제 처리 로 간주되지 않는다고 생각합니다 (우리의 목표는 코드가 무엇이든 상관없이 작동하지 않는 것입니다 ). 솔직히 말해서, 종종 null이 전달되면 적절한 블록을 작성하는 것을 잊어 버립니다. 그러나 NullArgumentException을 던지는 것이 가장 좋은 방법이라고 생각합니다. 제 생각에 최악의 방법은 null을 처리 할 수 ​​없지만 예외 결과를 throw하는 대신 메서드 결과 (또는 빈 문자열 또는 빈 컬렉션 또는 -1 반환 유형이 void 인 경우 메소드 실행을 건너 뜁니다.
empi

2
@Giorgio, ArgumentNullException문제가 무엇인지, 문제를 해결하는 방법을 매우 명확하게 만듭니다. C #은 "undefined"를 사용하지 않습니다. 정의되지 않은 방식으로 전화를 걸면 전화하는 방식이 의미가 없습니다. 이를 나타내는 올바른 방법은 예외입니다. 이제, 때로는 int(예를 들어) 파싱 할 수없는 경우 null을 반환하는 파싱 메소드를 만들 것 입니다. 그러나 구문 분석 할 수없는 결과가 사용 오류가 아닌 합법적 인 가능성 인 경우입니다. 이 기사 도 참조 하십시오 .
Kyralessa

35

확인하는 습관을 null내 경험으로는 그 언어로 당신이 때 심각한 오류를 숨기는 좋은 기회가 전 C 또는 C ++ 개발자에서 제공 하지 검사를 NULL. 아시다시피 Java 또는 C #에서는 상황이 다르므로 확인하지 않고 null ref를 사용 null하면 예외가 발생하므로 호출 측에서 무시하지 않는 한 오류가 비밀리에 숨겨지지 않습니다. 따라서 대부분의 경우 명시 적으로 검사 null하면 의미가 없으며 코드를 필요 이상으로 복잡하게 만듭니다. 그렇기 때문에 이러한 언어에서 null 확인을 좋은 습관으로 생각 하지 않는 이유는 무엇입니까 (일반적으로는 아닙니다).

물론 그 규칙에는 예외가 있습니다. 여기 제가 생각할 수있는 것들이 있습니다 :

  • 프로그램의 어느 부분에서 잘못된 널 참조가 발생했는지 알려주는 더 나은 사람이 읽을 수있는 오류 메시지가 필요합니다. 따라서 null 테스트는 다른 오류 텍스트와 함께 다른 예외를 throw합니다. 분명히, 그런 식으로 오류가 가려지지 않습니다.

  • 당신은 당신의 프로그램을 "더 일찍 실패하게"하고 싶다. 예를 들어 생성자 매개 변수의 null 값을 확인하려고합니다. 그렇지 않으면 개체 참조가 멤버 변수에만 저장되고 나중에 사용됩니다.

  • 후속 오류의 위험이없고 심각한 오류를 가리지 않고 null 참조의 상황을 안전하게 처리 할 수 ​​있습니다.

  • 현재 컨텍스트에서 완전히 다른 종류의 오류 신호 (예 : 오류 코드 반환)를 원합니다.


3
"당신은 더 나은 사람이 읽을 수있는 오류 메시지를 원합니다."-이것이 제 생각에는 그렇게하는 가장 좋은 이유입니다. "개체 참조가 개체의 인스턴스로 설정되지 않았습니다"또는 "널 참조 예외"는 "제품이 인스턴스화되지 않음"만큼 도움이되지 않습니다.
pdr

@ pdr : 확인해야 할 또 다른 이유는 항상 감지되는지 확인하는 것입니다.
조르지오

13
아마도 "이전 C 개발자"일 것입니다. "이전 C ++ 개발자"는 C 개발자를 스스로 복구하는 경우에만 해당됩니다. 현대 C ++ 개발자로서 나체 포인터 또는 무모한 동적 할당을 사용하는 코드에 대해 매우 의심 스러울 것입니다. 사실, 나는 논쟁을 돌리고 C ++이 Java와 C #이 가지고있는 추가 특수 null 속성을 가지고 있지 않기 때문에 OP가 설명하는 문제를 겪지 않을 것이라고 유혹하고 싶습니다 .
Kerrek SB

7
첫 번째 단락은 이야기의 절반에 지나지 않습니다. 그렇습니다. C #에서 null 참조를 사용하면 예외가 발생하지만 C ++에서는 정의되지 않은 동작이 발생합니다. 그러나 잘 작성된 C #에서는 잘 작성된 C ++보다 훨씬 많은 nullable 참조 가 붙어 있습니다.
James McNellis

캡슐화가 좋을수록이 답변이 더 잘 적용됩니다.
radarbob

15

어설 션 을 사용 하여 코드에 대한 사전 / 사후 조건 및 변형을 테스트하고 나타냅니다.

코드가 기대하는 것과 처리 할 수있는 것을 이해하기가 훨씬 쉽습니다.

어설트 IMO는 다음과 같은 이유로 적합한 검사입니다.

  • 효율적 (보통 릴리스에서 사용되지 않음)
  • 적요 (보통 한 줄)
  • clear (전제 조건을 위반하면 프로그래밍 오류입니다).

자체 문서화 코드가 중요하다고 생각하므로 확인하는 것이 좋습니다. 방어적이고 빠르며 프로그래밍이 쉬워 유지 보수가 쉬워집니다. 일반적으로 대부분의 시간이 소요됩니다.

최신 정보

정교하게 작성하십시오. 일반적으로 최종 사용자에게 버그 아래서 잘 작동하는 응용 프로그램을 제공하는 것이 좋습니다. 견고 함은 좋으며, 천국은 중요한 순간에 무엇이 깨질 지 알기 때문입니다.

그러나 개발자와 테스터는 가능한 빨리 버그를 알고 있어야하므로 문제가 있음을 사용자에게 경고 할 수있는 후크가있는 로깅 프레임 워크를 원할 것입니다. 경고는 모든 사용자에게 표시되어야하지만 런타임 환경에 따라 다르게 조정될 수 있습니다.


릴리스에 존재하지 않는다고 주장하는 것은 저에게는 큰 부정입니다. 릴리스는 추가 된 정보를 원하는 정확한 시간
jk입니다.

1
글쎄, 필요하다면 릴리즈에서도 활성화되는 assert ^ h ^ h ^ hconditional throw 함수를 자유롭게 사용하십시오. 나는 내 자신을 아주 자주 굴렸다.
Macke

7

일찍 실패하고 자주 실패하십시오. null를 사용하려고하면 빨리 실패 할 수 있기 때문에 예상치 못한 최고의 값 중 하나입니다. 다른 예상치 못한 값은 감지하기 쉽지 않습니다. null사용하려고 할 때마다 자동으로 실패 하므로 명시적인 검사가 필요하지 않습니다.


28
나는 당신이 의미 희망 : 빨리, 자주는 아니지만 가끔 ... 실패, 초기 실패
EMPI를

12
문제는 명시적인 검사 없이는 null 값이 예외가 발생하기 훨씬 전에 전파 될 수 있으며, 그 원인이 어디인지 찾기가 매우 어렵다는 것입니다.
Michael Borgwardt

@MichaelBorgwardt 스택 트레이스가 무엇입니까?
R0MANARMY

6
@ R0MANARMY : null 값이 일부 필드에 저장되고 NullPointerException이 훨씬 나중에 발생하면 스택 추적이 도움이되지 않습니다.
Michael Borgwardt

@ R0MANARMY 스택 추적에서 줄 번호를 신뢰할 수 있었더라도 (많은 경우는 불가능합니다) 일부 줄에는 null 일 수있는 여러 변수가 포함되어 있습니다.
하드 슈나이더

6
  • 널 확인은 두 번째 특성이어야합니다.

  • 귀하의 API는 다른 사람들이 사용하며 그들의 기대는 귀하의 것과 다를 수 있습니다

  • 철저한 단위 테스트는 일반적으로 null 참조 확인의 필요성을 강조

  • null 검사는 조건문을 의미하지 않습니다. null 검사로 코드를 읽을 수 없게되는 것이 걱정된다면 .NET 코드 계약을 고려할 수 있습니다.

  • 누락 된 null 참조 확인을 위해 코드를 검색하려면 ReSharper (또는 이와 유사한)를 설치하십시오


전제 조건으로 코드 계약을 사용하는 것은 단순히 조건문을 영화 롭게합니다.
CVn

예, 동의하지만 코드 계약을 사용할 때 코드가 더 깨끗해 보입니다. 즉 가독성이 향상됩니다.
CodeART

4

의미 론적 관점에서 질문을 고려할 것입니다. 즉, NULL 포인터 또는 null 참조 인수가 무엇을 나타내는 지 스스로에게 묻습니다.

함수 또는 메소드 foo ()에 T 유형의 인수 x가있는 경우 x는 필수 또는 선택적 일 수 있습니다 .

C ++에서는 다음 과 같이 필수 인수를 전달할 수 있습니다.

  • 값, 예 : void foo (T x)
  • 예를 들어 void foo (T & x).

두 경우 모두 NULL 포인터를 확인하는 데 문제가 없습니다. 그래서, 많은 경우에, 당신은 널 포인터 검사를 필요로하지 않는 모든 필수 인수.

선택적 인수를 전달하는 경우 포인터를 사용하고 NULL 값을 사용하여 값이 지정되지 않았 음을 나타낼 수 있습니다.

void foo(T* x)

대안은 같은 스마트 포인터를 사용하는 것입니다

void foo(shared_ptr<T> x)

이 경우 코드에서 포인터를 확인하고 싶을 것입니다. x가 값을 포함하는지 아니면 값이 없는지 foo () 메소드에 차이가 있기 때문입니다.

세 번째이자 마지막 사례는 필수 인수에 대한 포인터 를 사용한다는 것 입니다. 이 경우 x == NULL로 foo (x)를 호출하면 오류가 발생 하며이를 처리하는 방법을 결정해야합니다 (범위를 벗어난 인덱스 또는 유효하지 않은 입력과 동일).

  1. 확인 사항 없음 : 버그가있는 경우 충돌이 발생하여이 버그가 충분히 일찍 나타날 수 있기를 바랍니다 (테스트 중). 실패한 경우 (초기 실패에 실패한 경우) 사용자 경험에 따라 응용 프로그램 충돌이 발생하기 때문에 프로덕션 시스템에 표시되지 않기를 바랍니다.
  2. 메소드의 시작 부분에서 모든 인수를 확인하고 유효하지 않은 NULL 인수를보고하십시오. 예를 들어 foo ()에서 예외를 발생시키고 다른 메소드가 처리하도록하십시오. 아마도 가장 좋은 방법은 응용 프로그램에 내부 오류가 발생하여 종료 될 것이라는 대화 상자를 사용자에게 표시하는 것입니다.

더 좋은 실패 방법 (사용자에게 더 많은 정보 제공) 외에도 두 번째 접근법의 장점은 버그가 발견 될 가능성이 높다는 것입니다. 접근 방식 1 버그는 포인터를 참조하는 명령문이 실행 된 경우에만 나타납니다 (예 : if 문의 오른쪽 분기가 입력 된 경우). 따라서 접근법 2는 더 강력한 형태의 "실패 초기"를 제공합니다.

Java에서는 모든 개체가 항상 null 일 수있는 참조를 사용하여 전달되므로 선택 사항이 적습니다. 다시, 널이 선택적 인수의 NONE 값을 나타내는 경우 널 점검은 구현 논리의 일부일 것입니다.

null이 유효하지 않은 입력이면 오류가 발생하여 C ++ 포인터의 경우와 비슷한 고려 사항을 적용합니다. Java에서 널 포인터 예외를 포착 할 수 있으므로 위의 접근법 1은 메소드 foo ()가 모든 실행 경로에서 모든 입력 인수를 역 참조하는 경우 (접근하는 작은 메소드의 경우가 많음) 접근법 2와 동일합니다.

요약

  • C ++과 Java 모두에서 선택적 인수 가 null 인지 확인하는 것은 프로그램 논리의 일부입니다.
  • C ++에서는 항상 필수 인수 를 확인하여 프로그램이 강력한 방식으로 처리 할 수 ​​있도록 적절한 예외가 발생하는지 확인합니다.
  • Java (또는 C #)에서는 더 강력한 "실패"형식을 가지기 위해 실행 경로가없는 경우 필수 인수를 확인 합니다.

편집하다

자세한 내용은 Stilgar에게 감사드립니다.

귀하의 경우 메서드의 결과로 null이있는 것 같습니다. 다시 말하지만, 결정을 내리기 전에 먼저 메소드의 의미를 명확히하고 수정해야한다고 생각합니다.

따라서 m2 () 메소드를 호출하는 m1 () 메소드가 있으며 m2 ()는 일부 객체에 대한 참조 (Java) 또는 포인터 (C ++)를 리턴합니다.

m2 ()의 의미는 무엇입니까? m2 ()는 항상 null이 아닌 결과를 반환해야합니까? 이 경우 널 결과는 내부 오류 (m2 ())입니다. m1 ()에서 반환 값을 확인하면보다 강력한 오류 처리가 가능합니다. 그렇지 않으면 조만간 예외가있을 수 있습니다. 아마. 배포 후에가 아니라 테스트 중에 예외가 발생하기를 바랍니다. 질문 : m2 ()가 null을 반환하면 안되는 이유는 무엇입니까? 어쩌면 예외를 던져야합니까? m2 ()는 아마도 버그입니다.

대안은 null을 리턴하는 것은 메소드 m2 ()의 의미론의 일부입니다. 즉, 선택적 결과를 가지므로 메소드의 Javadoc 문서에 문서화되어야합니다. 이 경우 널값을 갖는 것이 좋습니다. 메소드 m1 ()은 다른 값으로 확인해야합니다. 여기에는 오류가 없지만 결과가 널인지 확인하는 것은 프로그램 논리의 일부일뿐입니다.


경우에 따라서는 null 인 인수가 아니 었습니다. 우리는 존재할 것으로 예상되었지만 실제로는 존재하지 않는 것에 대한 데이터베이스 호출을했습니다. 문제는 모든 객체가 존재하는지 확인해야하는지 또는 객체가 없을 것으로 예상되는 경우에만 확인해야하는지입니다.
Stilgar

따라서 메소드가 리턴 한 결과는 오브젝트에 대한 참조이고 널은 결과를 나타내는 데 사용 된 값입니다. NOT FOUND. 올바르게 이해합니까?
조르지오

추가 세부 정보로 질문을 업데이트했습니다.
Stilgar

3

내 일반적인 접근 방식은 처리 방법을 모르는 오류 상태를 테스트하지 않는 것 입니다. 그런 다음 각 특정 인스턴스에서 대답해야하는 질문이됩니다. 예기치 않은 값 이있을 경우 합리적인 조치를 취할 수null 있습니까?

다른 값 (빈 컬렉션, 말 또는 기본값 또는 인스턴스)을 대체하여 안전하게 계속할 수 있다면 반드시 그렇게하십시오. 하나의 이미지를 다른 이미지로 대체하는 월드 오브 워크래프트@Shahbaz의 예가 그 범주에 속합니다. 이미지 자체는 장식 일 뿐이며 기능적인 영향 은 없습니다 . (디버그 빌드에서는 대체가 발생했다는 사실에 주목하기 위해 비명을 지르는 색상을 사용하지만 이는 애플리케이션의 특성에 따라 크게 달라집니다.) 오류에 대한 세부 사항을 기록하십시오. 그렇지 않으면 알고 싶은 오류를 숨기 게됩니다. 사용자로부터 그것을 숨기는 것은 한 가지 일입니다 (다시 처리가 안전하게 계속된다고 가정). 개발자에게 그것을 숨기는 것은 또 다른 것입니다.

널값이있는 상태에서 안전하게 계속할 수 없으면 합리적인 오류로 실패합니다. 일반적으로 작업이 성공하지 못한다는 것이 명백해지면 전제 조건을 확인하는 것이 가능한 한 빨리 실패하는 것을 선호합니다. 즉시 실패하고 싶지는 않지만 가능한 한 오랫동안 실패를 연기하는 경우가 있습니다. 이러한 고려 사항은 예를 들어 사이드 채널 공격으로 인해 원하지 않는 정보가 노출 될 수있는 암호화 관련 응용 프로그램에서 발생합니다. 마지막으로, 일반적인 오류 처리기까지 오류가 발생하도록합니다.이 오류 처리기는 관련 세부 정보를 기록하고 사용자에게 멋진 오류 메시지를 표시합니다.

테스트 / QA / 디버그 빌드와 프로덕션 / 릴리스 빌드간에 적절한 응답이 크게 다를 수 있다는 점도 주목할 가치가 있습니다. 디버그 빌드에서는 매우 자세한 오류 메시지로 조기에 실패하고 문제가 있음을 완전히 알리고 사후 사후 문제를 해결하기 위해 수행해야 할 작업을 결정할 수 있습니다. 안전하게 복구 가능한 오류가 발생 했을 때 프로덕션 빌드 는 복구를 선호해야합니다.


물론 체인에는 일반적인 오류 처리기가 있습니다. 로그의 문제점은 매우 오랫동안 로그를 확인할 사람이 없다는 것입니다.
Stilgar

@Stilgar, 로그를 확인할 사람이 없으면 null 점검의 유무에 문제가 없습니다.
CVn

2
문제가 있습니다. 최종 사용자는 단순히 오류를 숨기고 로그에 기록하는 null 검사가 있으면 시스템이 오랫동안 올바르게 작동하지 않는다는 것을 알지 못할 수 있습니다.
Stilgar

@Stilgar는 전적으로 오류의 본질에 달려 있습니다. 내가 게시물에서 말했듯 이, 예상치 못한 null을 바꾸거나 무시해야 안전하게 계속할 수있는 경우 에만 . 릴리스 빌드에서 다른 이미지 대신 하나의 이미지를 사용하는 경우 (디버그 빌드에서 가능한 빨리 실패하고 문제가 분명 해짐) 계산에 대해 잘못된 결과를 반환하는 것과는 매우 다릅니다. (그것을 포함하기 위해 수정 된 답변.)
CVn

1
어떻게 든 오류를 기록하고보고 할 수 없다면 (정통한 사용자에게 너무 많이 의존하지 않고) 프로덕션 에서조차도 조기에 실패 할 것입니다. 배포 된 버그를 사용자로부터 숨기는 것이 한 가지입니다. 자신을 숨기는 것은 위험합니다. 또한 사용자가 미묘한 버그로 살도록 허용하면 응용 프로그램에 대한 자신감이 손상 될 수 있습니다. 때로는 총알을 물린하고 거기에 버그가 있었다 알려 더 하고 당신이 신속하게 해결있다.
멀린 모건-그레이엄

2

물론이 NULL되지 예상 하지만, 버그가 항상있다!

나는 당신이 그것을 기대하지 않는지 확인 해야 한다고 생각 NULL합니다. 예를 들어 보겠습니다 (Java로되어 있지는 않지만).

  • 월드 오브 워크래프트에서는 버그로 인해 개발자 중 한 사람의 이미지가 많은 물체의 큐브에 나타났습니다. 그래픽 엔진이 포인터를 기대 하지 않았다면 NULL충돌이 있었지만 사용자에게는 치명적이지 않은 (심지어 우스운) 경험으로 바뀌 었다고 상상해보십시오 . 최소한 연결 또는 저장 점을 예기치 않게 잃어 버리지 않았을 것입니다.

    NULL을 확인하면 충돌이 발생하지 않고 사례가 자동으로 처리 된 예입니다.

  • 은행원 프로그램을 상상해보십시오. 인터페이스는 일부 확인을 수행하고 입금을 수행하기 위해 입력 데이터를 기본 파트로 보냅니다. 핵심 부분 이 수신을 기대 하지 않으면 NULL인터페이스의 버그로 인해 트랜잭션에 문제가 발생할 수 있으며 중간에 약간의 돈이 손실 (또는 악화, 중복) 될 수 있습니다.

    "문제"에 의해 나는 (같이 충돌을 의미 충돌 사고 (예를 들어 프로그램이 C ++로 작성)하지 널 포인터 예외). 이 경우 NULL이 발생하면 트랜잭션을 롤백해야합니다 (물론 NULL에 대해 변수를 확인하지 않으면 불가능합니다).

나는 당신이 아이디어를 얻는 것을 확신합니다.

함수에 대한 인수의 유효성을 검사해도 코드가 복잡해지지 않습니다. 중간에 확산되지 않고 함수 상단에서 몇 가지 검사이므로 견고성이 크게 향상되기 때문입니다.

"프로그램이 사용자에게 실패했음을 알려주고 오류 로그를 생성 할 수있는 일관된 상태로 정상적으로 종료 또는 롤백"과 "액세스 위반"중 하나를 선택해야합니까? 즉 , 버그가 항상 존재하기 때문에 모든 것이 정확하다고 기대 하기보다는 모든 함수가 버그가있는 코드에 의해 호출되도록 준비해야 합니다.


두 번째 예는 요점을 반증합니다. 은행 거래에서 문제가 발생하면 거래가 중단됩니다. 정확하게 돈을 잃거나 복제 할 수있는 오류를 다룰 때입니다. null을 확인하는 것은 "고객이 null 돈을 보냅니다 ... 글쎄 $ 10를 보내겠습니다 (개발자 이미지와 동일)"
Stilgar

@ 스틸 거, 반대로! NULL 검사는 메시지와 함께 중단됩니다. NULL을 확인하지 않으면 충돌발생 합니다. 이제 트랜잭션 시작시 충돌이 나쁘지는 않지만 중간에 치명적일 수 있습니다. (은행 송금이 게임과 같은 오류를 처리해야한다고 말하지 않았습니다! 처리 해야 한다는 사실 )
Shahbaz

2
"트랜잭션"의 요점은 반을 완료 할 수 없다는 것입니다. :) 오류가 있으면 롤백됩니다.
Stilgar

이것은 트랜잭션 의미론이 실제로 필요한 모든 것에 대한 끔찍한 조언입니다. 모든 거래를 원자적이고 dem 등원으로 만들어야합니다. 그렇지 않으면 오류가 발생하거나 중복 된 자원 (돈)을 보증하는 오류가 발생합니다. 비 원자 또는 비등 전성 연산을 처리해야하는 경우 원자 및 dem 등식 동작을 보장하는 레이어로 처리해야합니다 (이러한 레이어를 직접 작성해야하더라도). 항상 생각하십시오 :이 거래가 진행되는 순간 유성이 웹 서버에 충돌하면 어떻게됩니까?
멀린 모건-그레이엄

1
@ MerlynMorgan-Graham, 나는 그렇게했다. 혼란이 사라지 길 바랍니다.
Shahbaz

2

다른 답변이 지적했듯이 기대하지 않는다면 NULL을 던져서 명확하게하십시오 ArgumentNullException. 내 생각에 프로젝트를 디버깅 할 때 프로그램 논리의 결함을 빨리 발견하는 데 도움이됩니다.

따라서 소프트웨어를 릴리스 할 예정입니다. NullRefrences검사 를 복원하면 소프트웨어 의 논리에 대한 내용을 놓치지 않고 심각한 버그가 발생하지 않습니다.

또 다른 요점은 NULL점검이 기능의 매개 변수에 관한 것이면, 기능이 올바른 기능인지 아닌지를 결정할 수 있다는 것입니다. 그렇지 않은 경우 함수에 대한 모든 참조를 찾은 다음 매개 변수를 함수 검토에 전달하기 전에 널 참조로 이어질 수있는 가능한 시나리오를 검토하십시오.


1

null시스템의 모든 것은 발견되기를 기다리는 시한 폭탄입니다. null코드가 제어하지 않는 코드와 상호 작용하는 경계를 확인하고 null자신의 코드 내에서 금지 하십시오. 이것은 외래 코드를 순진 히 믿지 않고 문제를 제거합니다. 대신 예외 또는 일종의 Maybe/ Nothing유형을 사용하십시오.


0

널 포인터 예외는 결국 발생하지만 널 포인터가있는 지점에서 멀리 발생하는 경우가 많습니다. 최소한 널 포인터를 가능한 빨리 기록한다는 사실을 최소한 기록하여 버그를 수정하는 데 시간을 단축하고 유리한 입장을 취하십시오.

지금해야 할 일을 모르더라도 이벤트를 기록하면 나중에 시간을 절약 할 수 있습니다. 그리고 아마도 티켓을 얻은 사람은 절약 된 시간을 사용하여 적절한 응답을 찾을 수있을 것입니다.


-1

이후 Fail early, fail fast웹 응용 프로그램은 당신이 규칙을 시행해야 버그에서 잘 작동해야하기 때문에 같은 @DeadMG (@empi)에 의해 언급은 pssible 아니다

로깅없이 예외 잡기 없음

개발자는 로그를 검사하여 잠재적 인 문제를 알고 있습니다.

log4net 또는 log4j와 같은 로깅 프레임 워크를 사용하는 경우 오류 및 치명적 오류를 메일로 보내는 추가 특수 로깅 출력 (별칭) 설정할 수 있습니다 . 그래서 당신은 정보를 유지 합니다.

[최신 정보]

페이지의 최종 사용자가 오류를 인식하지 않으면 오류 및 치명적인 오류를 계속 알려주는 추가자를 설정할 수 있습니다.

페이지의 최종 사용자에게 암호 오류 메시지가 표시되면 페이지 처리의 오류 로그를 페이지 끝에 배치하는 어 펜더를 설정할 수 있습니다.

예외를 삼킨 경우 로깅을 사용하는 방법에 관계없이 프로그램은 의미있는 데이터를 계속 사용하고 삼키는 것이 더 이상 조용하지 않습니다.


1
문제는 페이지에서 어떻게됩니까?
Stilgar

데이터가 의미가 있고 시스템이 일관된 상태임을 어떻게 알 수 있습니까? 모든 오류가 예상치 못한 결과가되었으므로 영향이 무엇인지 알 수 없습니다.
Stilgar

"초기 실패 이후 @DeadMG (@empi)에 명시된대로 빠르게 실패하는 것은 웹 애플리케이션이 규칙을 적용해야하는 버그에서 제대로 작동해야하기 때문에 불가능합니다." 일부 오류 페이지로 리디렉션하십시오. 이것이 가능하지 않습니까?
Giorgio

-1

이 질문에 대한 좋은 대답이 이미 있으며 그 중 하나가 추가 될 수 있습니다. 내 코드에서 어설 션을 선호합니다. 코드가 유효한 객체로만 작동 할 수 있지만 너트가 null 인 경우 null 값을 지정해야합니다.

이러한 상황에서 어설 션을 수행하면 모든 단위 및 / 또는 통합 테스트가 정확한 메시지로 실패 할 수 있으므로 생산 전에 문제를 매우 빠르게 해결할 수 있습니다. 이러한 오류가 테스트를 통과하고 생산 (어설 션을 비활성화 해야하는 곳)으로 이동하면 NpEx와 심각한 버그가 발생하지만 훨씬 더 희미하고 다른 곳에서 발생하는 작은 버그보다 낫습니다. 코드를 수정하는 데 비용이 많이 듭니다.

더 나아가 누군가 코드 어설 션으로 작업해야하는 경우 코드를 디자인 / 작성할 때 수행 한 가정 (이 경우 : 이봐,이 개체를 제시해야합니다!)에 대해 가정하면 유지 관리가 쉬워집니다.


투표에 쓸 내용을 적어 주시겠습니까? 토론 해 주셔서 감사합니다. 고맙습니다
GET

나는 투표하지 않았고, 당신의 일반적인 생각에 동의합니다. 그래도 언어는 약간의 작업이 필요합니다. 어설 션은 API 계약을 정의하며 계약을 위반하면 조기 / 실패로 실패합니다. 애플리케이션 스킨에서이를 수행하면 코드 사용자에게 코드에서 무언가 잘못되었음을 알릴 수 있습니다. 코드 리 세스에서 스택 추적이 발견되면 코드가 버그라고 생각할 것입니다. 탄탄한 재현을 얻고 디버깅 할 때까지 그렇게 생각할 수도 있습니다.
멀린 모건-그레이엄

-1

일반적으로 null을 확인하지만 null이 발생한 정확한 위치에 대한 자세한 정보를 제공하는 예외를 발생시켜 예기치 않은 null을 "처리"합니다.

편집 : 무언가 잘못되었다고 생각하지 않을 때 null이 발생하면 프로그램이 죽어야합니다.


-1

예외의 언어 구현에 따라 다릅니다. 예외로 인해 시스템이 예상치 못한 상태 또는 상태에 진입 한 경우 데이터 손상을 방지하거나 리소스를 완전히 해제하기위한 조치를 취할 수 있습니다. 이것은 예상 할 수있는 조건 인 오류 처리와 다릅니다. 저의 철학은 가능한 적은 예외 처리가 있어야한다는 것입니다. 소음입니다. 예외를 처리하여 메소드가 합리적인 작업을 수행 할 수 있습니까? 회복 될 수 있습니까? 추가 손상을 수리하거나 방지 할 수 있습니까? 예외를 잡아서 메소드에서 돌아 오거나 다른 무해한 방식으로 실패하는 경우 실제로 예외를 포착하는 데 아무런 의미가 없습니다. 분석법에 노이즈와 복잡성 만 추가했을뿐 아니라 설계 결함의 원인을 숨길 수 있습니다. 이 경우 결함 버블이 일어나 외부 루프에 걸리게됩니다. 객체를 복구하거나 잠금으로 표시하거나 잠금 파일과 같은 중요한 리소스를 해제하거나 소켓을 완전히 닫는 등의 작업을 수행 할 수 있다면 예외를 잘 사용하는 것입니다. 실제로 NULL이 유효한 엣지 케이스로 자주 나타날 것으로 예상되는 경우 if-the-else 또는 switch-case 문 또는 예외 처리가 아닌 다른 방법으로 일반적인 논리 흐름에서이를 처리해야합니다. 예를 들어, 양식에서 비활성화 된 필드는 NULL로 설정 될 수 있으며, 이는 빈 필드를 나타내는 빈 문자열과 다릅니다. 이것은 올바른 판단과 상식을 사용해야하는 영역입니다. 나는 모든 상황에서이 문제를 처리하는 방법에 대한 탄탄한 규칙을 들어 본 적이 없다. 객체를 복구하거나 잠금으로 표시하거나 잠금 파일과 같은 중요한 리소스를 해제하거나 소켓을 완전히 닫는 등의 작업을 수행 할 수 있다면 예외를 잘 사용하는 것입니다. 실제로 NULL이 유효한 엣지 케이스로 자주 나타날 것으로 예상되는 경우 if-the-else 또는 switch-case 문 또는 예외 처리가 아닌 다른 방법으로 일반적인 논리 흐름에서이를 처리해야합니다. 예를 들어, 양식에서 비활성화 된 필드는 NULL로 설정 될 수 있으며, 이는 빈 필드를 나타내는 빈 문자열과 다릅니다. 이것은 올바른 판단과 상식을 사용해야하는 영역입니다. 나는 모든 상황에서이 문제를 처리하는 방법에 대한 탄탄한 규칙을 들어 본 적이 없다. 객체를 복구하거나 잠금으로 표시하거나 잠금 파일과 같은 중요한 리소스를 해제하거나 소켓을 완전히 닫는 등의 작업을 수행 할 수 있다면 예외를 잘 사용하는 것입니다. 실제로 NULL이 유효한 엣지 케이스로 자주 나타날 것으로 예상되는 경우 if-the-else 또는 switch-case 문 또는 예외 처리가 아닌 다른 방법으로 일반적인 논리 흐름에서이를 처리해야합니다. 예를 들어, 양식에서 비활성화 된 필드는 NULL로 설정 될 수 있으며, 이는 빈 필드를 나타내는 빈 문자열과 다릅니다. 이것은 올바른 판단과 상식을 사용해야하는 영역입니다. 나는 모든 상황에서이 문제를 처리하는 방법에 대한 탄탄한 규칙을 들어 본 적이 없다.

메소드에 사용 된 오브젝트에 의해 발생할 수있는 모든 예외가 메소드의 "throws"절에서 발견되거나 선언되어야하는 "체크 된 예외"로 Java가 예외 처리 구현에 실패합니다. 이것은 C ++과 Python의 반대이며, 적절하다고 생각되는 예외를 처리하도록 선택할 수 있습니다. Java에서 예외를 발생시킬 수있는 호출을 포함하도록 메소드 본문을 수정하는 경우 코드에 노이즈 및 복잡성을 추가하지 않아도되는 예외에 대한 명시 적 처리를 추가 할 수 있습니다. 노이즈와 클러 터를 추가 할뿐만 아니라 메소드의 서명을 변경하는 수정하는 메소드의 "throws"절에 예외를 추가하십시오. 그런 다음 메소드가 새 예외를 처리하는 데 사용되는 모든 위치에서 수정 코드로 이동해야합니다. 이제 메소드가 해당 메소드의 "throws"절에 예외를 추가하거나 추가하여 코드 변경의 도미노 효과를 트리거합니다. "캐치 또는 지정 요구 사항"은 Java의 가장 성가신 측면 중 하나 여야합니다. RuntimeException에 대한 예외 예외와이 설계 실수에 대한 공식 Java 합리화는 절름발이입니다.

마지막 단락은 질문에 답하는 것과는 거의 관련이 없습니다. 나는 단지 자바를 싫어한다.

-노아


이 게시물은 읽기 어렵습니다 (텍스트의 벽). 더 나은 형태로 편집 하시겠습니까 ?
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.