2009-03-13 8 views
7

nullable이 아닌 참조 타입이 많은 버그를 해결하고 프로그래밍을 훨씬 쉽게하는 방법에 대해 사람들이 계속 이야기하는 것을 계속 들었습니다. null의 작성자조차도 billion dollar mistake이라고하고 Spec#은이 문제를 해결할 수있는 nullable이 아닌 유형을 도입했습니다.nullable이 아닌 타입 논쟁에 관해서

편집 : 사양 #에 대한 내 의견을 무시하십시오. 나는 그것이 어떻게 작동하는지 오해했다.

편집 2 : 누군가가 그래서 생각 것 :-)


과 논쟁하는 내가 잘못된 사람들에게 얘기해야한다, 나는 정말 소수에있는, 기대했다 나는 틀렸다고 생각하지만, 왜이 논쟁에는 장점이 있는지 이해할 수 없습니다. 버그 찾기 도구로 null이 표시됩니다. 다음을 고려하십시오.

class Class { ... } 

void main() { 
    Class c = nullptr; 
    // ... ... ... code ... 
    for(int i = 0; i < c.count; ++i) { ... } 
} 

BAM! 액세스 위반. 누군가 c을 초기화하는 것을 잊었습니다.


지금이 고려 :

class Class { ... } 

void main() { 
    Class c = new Class(); // set to new Class() by default 
    // ... ... ... code ... 
    for(int i = 0; i < c.count; ++i) { ... } 
} 

으악합니다. 루프는 자동으로 건너 뜁니다. 문제를 추적하는 데 시간이 걸릴 수 있습니다.


클래스가 비어 있으면 코드가 실패합니다. 시스템이 자신을 알아내는 대신에 (다소 우스꽝 스럽지만) 당신에게 알려주지 않겠습니까?

+0

다른 사람들이 널 보게되어 반가워요, 나는 아직 학교에있어 그래서 내가 놓친 것이 있다고 가정합니다. –

+1

"가치 없음"을 다루는보다 원칙적인 방법이 있습니다. NULL는 int와 같은 기본 유형을 제외합니다. 형식 시스템이 참조에 대해 암시 적으로 만 사용하는 대신 모든 유형에서 일관되게 가치 부족을 나타내는 것이 더 좋습니다. Haskell의 "Maybe"및 ML/OCaml/F #의 "option"유형을 참조하여 수행 방법을 확인하십시오. – MichaelGG

+0

[null이없는 언어에 대한 최선의 설명] 가능한 복제본 (http://stackoverflow.com/questions/3989264/best-explanation-for-languages-without-null) – nawfal

답변

-2

저는 NULL을 사용합니다. C++로 작업 한 이래로 얼마간의 시간이 걸렸지 만 반환 값 및 참조와 관련된 문제를 쉽게 찾고 수정할 수있었습니다.

+0

요점을 놓치십시오. –

2

필자는 SpeC#에 대해 많이 읽지는 ​​않았지만 NonNullable은 본질적으로 변수 선언에있는 매개 변수에 넣는 특성이라는 것을 알고있었습니다. 같은으로 예를 돌

규격 번호와
class Class { ... } 

void DoSomething(Class c) 
{ 
    if (c == null) return; 
    for(int i = 0; i < c.count; ++i) { ... } 
} 

void main() { 
    Class c = nullptr; 
    // ... ... ... code ... 
    DoSomething(c); 
} 

, 당신은 "매개 변수 C는 null 일 수 없습니다"말을 해봐요을 표시하고 있습니다. DoSomething() 메서드 (DoSomething()의 컨텍스트에 완전히 의미가없는)를 잊어 버리기 쉽습니다.) 메서드에서 첫 번째 줄이 필요하지 않음을 의미하므로 나에게 좋은 기능인 것처럼 보입니다.

6

귀하의 예를 이해할 수 없습니다. "= new Class()"가 널 (NULL)을 갖지 않는 자리 표시자인 경우, 분명히 버그입니다. 그렇지 않은 경우 실제 버그는 "..."이 내용을 올바르게 설정하지 않았고 두 경우 모두 정확히 동일합니다.

c를 초기화하는 것을 잊었다는 것을 보여주는 예외는 초기화되지 않은 지점을 알려주지 만 초기화해야하는 위치는 알려주지 않습니다. 비슷하게, 놓친 루프는 .count가 0이 아닌 곳에 있어야 할 곳을 알려주지 만, 끝내야 할 부분이나 어디에 있어야 하는지를 알려주지 않습니다. 나는 어느 쪽이든이 프로그래머에게 더 쉬운 것으로 보이지 않는다.

"null이 아닌"점은 단순히 텍스트 찾기 및 바꾸기를 수행하여 빈 인스턴스로 만드는 것이라고 생각하지 않습니다. 그건 분명히 쓸모가 없다.요점은 코드가 구조화되어 변수가 쓸모 없거나 잘못된 값을 가리키는 상태가 아니므로 NULL이 가장 일반적인 것입니다.

+0

그래서 언어가 nullable 타입을 구현하지 않으면 "Class c;" c를? (나는 당신의 의견에 완전히 동의한다) – zildjohn01

+2

나의 첫 번째 생각 : 그것이 허용되면 안된다. 논리적으로 말하면 "Foo를 저장해야하는 슬롯을 만듭니다 (하지만 비워 둡니다").이것은 아마도 Lisp이나 Ruby와 같은 모든 표현식을 필요로 할 것입니다. - 할당을하기 위해 순차적 인 문장을 실행해야한다면 어떻게 될지 모르겠습니다. – Ken

+1

돌이켜 보면 : SQL을 생각해보십시오. "NOT NULL"열을 가지고 있고 null이 아닌 값을 지정하지 않고 레코드를 삽입하려고하면 단순히 오류가 발생합니다. – Ken

0

null을 사용하는 두 영역이 있습니다.

첫 번째 값은 없습니다. 예를 들어, 부울은 참 또는 거짓이거나 사용자가 아직 설정을 선택하지 않았으므로 null 일 수 있습니다. 이것은 유용하고 좋은 일이지만, 아마도 원래 잘못 구현되어 아마도 그 사용을 형식화하려는 시도가 있습니다. (set/unset 상태를 유지하는 두 번째 부울이 있거나 3 상태 논리의 일부로 null이 있어야합니다.)

두 번째는 널 포인터 의미입니다. 이것은 프로그램 오류 상황보다 더 자주 발생합니다. 예외. 의도 한 상태가 아니며 프로그램 오류가 있습니다. 이는 현대 언어로 구현 된 공식적인 예외의 산물이어야합니다. 즉, try/catch 블록을 통해 NullException이 발견되었습니다.

그렇다면이 중 어떤 것에 관심이 있습니까?

+0

# 2. – zildjohn01

2

null이 아닌 유형의 아이디어는 클라이언트가 아닌 컴파일러가 버그를 찾는 것입니다. 언어에 @nullable (null 일 수 있음)과 @nonnull (null이 아님)의 두 가지 유형 지정자를 추가한다고 가정합니다 (Java 주석 구문을 사용하고 있습니다).

함수를 정의 할 때 인수를 주석으로 지정합니다. 예를 들어, 다음 코드는 foo는이 항목이 foo.size()를 호출 할 때, foo는이 널 (null)이 아닌하다는 것을 제어 보장의 흐름에 null이 될 수 있지만

int f(@nullable Foo foo) { 
    if (foo == null) 
    return 0; 
    return foo.size(); 
}

을 컴파일합니다.

그러나 null 검사를 제거하면 컴파일 타임 오류가 발생합니다.

foo는이 항목에서 널 (null)이 아닌 있기 때문에 다음도 컴파일

:

@nullable Foo foo; 
g(foo); // compiler error!

이 컴파일러는 않습니다 :

int g(@nonnull Foo foo) { 
    return foo.size(); // OK 
}

그러나, 당신이 널 (NULL) 포인터와 g를 호출 할 수 없습니다 모든 함수에 대한 플로우 분석을 제공하므로 @nullable이 @nonnull이 될 때를 감지 할 수 있습니다 (예 : null을 확인하는 if 문 내부). 또한 즉시 초기화되는 경우 @nnnull veriable 정의를 허용합니다.

@nonnull Foo foo = new Foo();

my blog에서이 주제에 대한 훨씬 더있다.

0

현재이 주제는 C#에서 작업하고 있습니다. .NET에는 값 형식에 대해 Nullable이 있지만 참조 형식에 대해서는 역방향 특성이 없습니다.

참조 유형에 대해 NotNullable을 생성하고 문제를 (더 이상 null 검사가 필요 없음) 데이터 유형 도메인으로 옮겼습니다. 이렇게하면 응용 프로그램이 컴파일 타임이 아닌 런타임에 예외를 throw합니다.

12

그것의 응답이 스레드가 실제로 즉 처음에 널 (null)로 문제를 강조에 "답"을 표시하는 작은 홀수 :

나는 또한 발견했습니다 내 NULL 포인터 오류의 대부분이 함수를 잊어 버리는 것에서 string.h의 함수가 반환되는지 확인하십시오. 여기서 NULL은 표시기로 사용됩니다.

컴파일러가 런타임이 아닌 컴파일 타임에 이러한 종류의 오류를 잡을 수 있다면 좋지 않겠습니까?

ML과 비슷한 언어 (SML, OCaml, SML 및 F # 어느 정도까지) 또는 하스켈을 사용한 경우 참조 유형은 null을 사용할 수 없습니다. 대신, 옵션 유형을 "null"값으로 나타냅니다. 이런 식으로 합법적 인 값으로 null을 반환 할 수있는 경우 실제로 함수의 반환 형식을 변경합니다. 그래서, 나는 데이터베이스에서 사용자를 끌어 원하는 가정 해 봅시다 :

let findUser username = 
    let recordset = executeQuery("select * from users where username = @username") 
    if recordset.getCount() > 0 then 
     let user = initUser(recordset) 
     Some(user) 
    else 
     None 

찾기 사용자 유형 val findUser : string -> user option이 있으므로 함수의 반환 형식은 실제로는 null 값을 반환 할 수 있음을 알려줍니다. 코드를 소비하려면 모두 일부 및 없음 경우를 처리 할 필요가 : 당신이 두 경우를 처리하지 않는 경우, 코드도 컴파일되지 않습니다

match findUser "Juliet Thunderwitch" with 
| Some x -> print_endline "Juliet exists in database" 
| None -> print_endline "Juliet not in database" 

. 따라서 타입 시스템은 널 참조 예외를 결코 얻지 못하도록 보장하며, 널 (null)을 항상 처리하도록 보장합니다. 그리고 함수가 user을 반환하면 그 객체의 실제 인스턴스로 보장됩니다. 감사합니다.

class Class { ... } 

void main() { 
    Class c = new Class(); // set to new Class() by default 
    // ... ... ... code ... 
    for(int i = 0; i < c.count; ++i) { ... } 
} 

이 초기화 및 초기화되지 않은 객체가 같은 데이터 유형을 가지고, 당신은 그들 사이의 차이를 말할 수 없습니다

이제 우리는 OP의 샘플 코드에서 문제를 참조하십시오. 경우에 따라 null object pattern이 유용 할 수 있지만 위의 코드는 형식을 올바르게 사용하고 있는지 여부를 컴파일러에서 확인할 수있는 방법이 없음을 보여줍니다.

0

도메인 객체를 처리 할 때 Null을 허용하지 않는 유형이 더 적합합니다. 데이터베이스 테이블을 오브젝트에 맵핑 할 때 널 (null)이 아닌 컬럼이있을 때. User라는 테이블이 있고 nullable이 아닌 열 userid varchar (20)가 있다고 가정합니다.

nullable이 아닌 UserId 문자열 필드가있는 User 클래스를 사용하면 편리합니다. 컴파일 할 때 몇 가지 버그를 줄일 수 있습니다.