IT TIP

C # 및 Java에 "null"이있는 이유는 무엇입니까?

itqueen 2020. 10. 22. 23:49
반응형

C # 및 Java에 "null"이있는 이유는 무엇입니까?


C # (또는 Java)로 개발 된 소프트웨어의 많은 버그로 인해 NullReferenceException이 발생하는 것을 발견했습니다.

"null"이 언어에 포함 된 이유가 있습니까?

결국 "null"이 없으면 버그가 없을 것입니다. 그렇죠?

즉, 언어의 어떤 기능이 null 없이는 작동하지 않습니까?


"C # 아버지"인 Anders Hejlsberg 는 Computerworld 인터뷰 에서 그 점에 대해 방금 말했습니다 .

예를 들어, 유형 시스템에서는 값과 참조 유형을 구분하지 않으며 유형의 null 허용 여부가 없습니다. 약간 이상하거나 기술적으로 들릴 수 있지만 C #에서는 문자열과 같은 참조 형식이 null 일 수 있지만 값 형식은 null 일 수 없습니다. nullable이 아닌 참조 유형이 있으면 좋을 것이므로 '이 문자열은 절대로 null이 될 수 없으며 컴파일러가 여기서 null 포인터를 칠 수 없는지 확인하기를 원합니다.'라고 선언 할 수 있습니다.

오늘날 사람들이 직면하는 버그의 50 %는 우리 플랫폼에서 C #으로 코딩하고 Java에서도 마찬가지입니다. 이는 아마도 null 참조 예외 일 것입니다. 우리가 '이 매개 변수는 절대 null이 아닐 수 있으며 컴파일러는 코드의 정적 분석을 수행하여 모든 호출에서이를 확인하십시오'라고 말할 수있는 더 강력한 유형 시스템이 있다면. 그런 다음 버그 클래스를 제거 할 수 있습니다.

C # 팀의 전 소프트웨어 디자인 엔지니어 (현재 Google에서 근무 중) 인 Cyrus Najmabadi는 자신의 블로그 ( 1st , 2nd , 3rd , 4th ) 에서 해당 주제에 대해 논의합니다 . nullable이 아닌 유형을 채택하는 데 가장 큰 장애는 표기법이 프로그래머의 습관과 코드 기반을 방해한다는 것입니다. C # 프로그램에 대한 참조의 70 %와 같은 것은 Null이 불가능한 것으로 끝날 가능성이 높습니다.

C #에서 nullable이 아닌 참조 유형을 실제로 사용하려면 "!"사용을 허용하는 C # 확장 인 Spec # 을 사용해야합니다. 널 불가능한 기호로.

static string AcceptNotNullObject(object! s)
{
    return s.ToString();
}

Nullity는 참조 유형의 자연스러운 결과입니다. 참조가있는 경우 일부 개체를 참조하거나 null이어야합니다. nullity를 금지하려면 항상 모든 변수가 null이 아닌 표현식으로 초기화되었는지 확인해야하며 초기화 단계에서 변수를 읽은 경우에도 문제가 발생합니다.

무효의 개념 제거를 어떻게 제안 하시겠습니까?


객체 지향 프로그래밍의 많은 것들과 마찬가지로 모든 것이 ALGOL로 돌아갑니다. Tony Hoare는이를 "10 억 달러의 실수"라고 불렀습니다. 어쨌든 그것은 과소 평가입니다.

다음은 Java에서 null 허용 여부를 기본값으로 만드는 방법에 대한 정말 흥미로운 논문 입니다. C #과의 유사점은 분명합니다.


C #의 Null 은 대부분 메모리에있는 어떤 것도 가리 키지 않는 포인터 (또는 오히려 adress 0x00) 가있는 C ++의 이월 입니다. 에서 인터뷰 , 앤더스 헤 즐스 버그는 C #에서 nullable이 아닌 참조 유형을 추가하는 것처럼 그는했습니다 것이라고 말했다.

Null은 또한 유형 시스템에서 합법적 인 위치를 차지하지만 맨 아래 유형 (위 object맨 위 유형) 과 비슷합니다 . lisp에서 하단 유형은 NIL이고 Scala에서는 Nothing.

어떤 널없이 C # .NET을 설계 할 수 있었던 것입니다하지만 당신은 사람들이 일반적으로 위해 가지고있는 용도에 대한 수용 가능한 해결책을 마련 할 것 null같은, unitialized-value, not-found, default-value, undefined-value,와 None<T>. 어쨌든 C ++ 및 Java 프로그래머가 성공했다면 아마도 채택이 줄어들었을 것입니다. 적어도 C # 프로그램에 널 포인터 예외가 없다는 것을 알기 전까지는 말입니다.


null을 제거하면 많이 해결되지 않습니다. init에 설정된 대부분의 변수에 대한 기본 참조가 필요합니다. null 참조 예외 대신 변수가 잘못된 개체를 가리 키기 때문에 예기치 않은 동작이 발생합니다. 적어도 null 참조는 예기치 않은 동작을 일으키는 대신 빠르게 실패합니다.

이 문제의 일부를 해결하는 방법은 null-object 패턴을 볼 수 있습니다.


Null은 매우 강력한 기능입니다. 가치가 없으면 어떻게합니까? NULL입니다!

한 학교는 절대 null을 반환하지 않는 것이고 다른 학교는 항상 반환하는 것입니다. 예를 들어 어떤 사람들은 유효하지만 빈 객체를 반환해야한다고 말합니다.

나는 그것이 실제로 무엇인지에 대한 진정한 표시이기 때문에 null을 선호합니다. 내 지속성 레이어에서 항목을 검색 할 수없는 경우 null을 원합니다. 빈 값을 원하지 않습니다. 하지만 그게 나야.

프리미티브에 특히 유용합니다. 예를 들어 true 또는 false가 있지만 보안 양식에서 사용되는 경우 권한은 허용, 거부 또는 설정되지 않을 수 있습니다. 글쎄, 나는 그것을 null로 설정하지 않기를 원합니다. 그래서 부울을 사용할 수 있습니까?

더 많은 것을 할 수 있지만 거기에 남겨 둘 것입니다.


결국 "null"이 없으면 버그가 없을 것입니다. 그렇죠?

대답은 아니오 입니다. 문제는 C #이 null을 허용한다는 것이 아니라 NullReferenceException으로 자신을 드러내는 버그가 있다는 것입니다. 이미 언급했듯이 null은 언어에서 "빈"참조 유형 또는 값이 아닌 (비어 있음 / 없음 / 알 수 없음)을 나타내는 목적을 가지고 있습니다.


Null은 NullPointerExceptions를 일으키지 않습니다 ...

프로그래머가 NullPointerException을 발생시킵니다.

null이 없으면 실제 임의 값을 사용하여 함수 나 메서드의 반환 값이 유효하지 않은지 확인합니다. 반환 된 -1 (또는 무엇이든)을 확인해야합니다. null을 제거하면 게으름이 마술처럼 해결되지는 않지만 약간 난독 화됩니다.


질문은 "각 참조 유형 (예 : String.Empty) 또는 null에 대한 기본값을 갖는 것이 더 나은가?"로 해석 될 수 있습니다. 이 관점에서 나는 null을 선호합니다.

  • 내가 작성하는 각 클래스에 대해 기본 생성자를 작성하고 싶지 않습니다.
  • 이러한 기본값에 대해 불필요한 메모리가 할당되는 것을 원하지 않습니다.
  • 참조가 null인지 확인하는 것이 값 비교보다 저렴합니다.
  • NullReferanceExceptions 대신 감지하기 어려운 버그가 더 많이있을 가능성이 높습니다. 내가 뭔가 잘못하고 있음을 분명히 나타내는 예외가 있다는 것은 좋은 일입니다.

값 유형과 참조 유형이 있으므로 "Null"이 언어에 포함됩니다. 아마도 부작용 일 수 있지만 좋은 것 같아요. 그것은 우리가 메모리를 효과적으로 관리하는 방법에 대한 많은 힘을줍니다.

왜 우리는 null이 있습니까? ...

값 유형은 "스택"에 저장되며 해당 값은 해당 메모리에 직접 저장됩니다 (즉, int x = 5는 해당 변수의 메모리 위치에 "5"가 포함되어 있음을 의미 함).

반면 참조 유형 에는 힙 실제 값을 가리키는 스택의 "포인터"가 있습니다 (예 : 문자열 x = "ello"는 스택의 메모리 블록에 힙의 실제 값을 가리키는 주소 만 포함됨을 의미) ).

null 값은 단순히 스택의 값이 힙의 실제 값을 가리 키지 않음을 의미합니다. 빈 포인터입니다.

내가 충분히 잘 설명했기를 바랍니다.


'NullReferenceException'이 발생하면 더 이상 존재하지 않는 개체를 계속 참조 할 수 있습니다. 이것은 'null'의 문제가 아니라 존재하지 않는 주소를 가리키는 코드의 문제입니다.


null 이 참조가 초기화되지 않았 음을 나타내는 좋은 방법 인 상황 이 있습니다. 이것은 일부 시나리오에서 중요합니다.

예를 들면 :

MyResource resource;
try
{
  resource = new MyResource();
  //
  // Do some work
  //
}
finally
{
  if (resource != null)
    resource.Close();
}

이것은 대부분의 경우 using 문을 사용하여 수행됩니다 . 그러나 패턴은 여전히 ​​널리 사용됩니다.

NullReferenceException과 관련하여 모든 매개 변수가 유효성을 검사하는 코딩 표준을 구현하면 이러한 오류의 원인을 쉽게 줄일 수 있습니다. 프로젝트의 성격에 따라 대부분의 경우 노출 된 멤버의 매개 변수를 확인하는 것으로 충분하다는 것을 알았습니다. 매개 변수가 예상 범위 내에 있지 않으면 사용중인 오류 처리 패턴에 따라 일종의 ArgumentException 이 발생하거나 오류 결과가 반환됩니다.

매개 변수 검사는 그 자체로 버그를 제거하지는 않지만 발생하는 모든 버그는 테스트 단계에서 찾아서 수정하기가 더 쉽습니다.

참고로 Anders Hejlsberg 는 C # 1.0 사양의 가장 큰 실수 중 하나로 non-null 적용의 부족을 언급했으며 현재이를 포함하는 것이 "어렵다"고 말했습니다.

여전히 정적으로 시행되는 null이 아닌 참조 값이 매우 중요하다고 생각한다면 spec # 언어를 확인할 수 있습니다. null이 아닌 참조가 언어의 일부인 C #의 확장입니다. 이렇게하면 null이 아닌 것으로 표시된 참조에 null 참조를 할당 할 수 없습니다.


한 응답은 데이터베이스에 null이 있다고 언급했습니다. 사실이지만 C #의 null과는 매우 다릅니다.

C #에서 null은 아무것도 참조하지 않는 참조에 대한 마커입니다.

데이터베이스에서 null은 값을 포함하지 않는 값 셀에 대한 마커입니다. 값 셀이란 일반적으로 테이블에서 행과 열의 교차를 의미하지만 값 셀의 개념은 테이블을 넘어서 확장 될 수 있습니다.

두 사람의 차이는 언뜻보기에는 사소한 것 같습니다. 하지만 그렇지 않습니다.


C # / C ++ / Java / Ruby에서 사용할 수있는 Null은 오늘날까지 살아남은 모호한 과거 (Algol)의 이상 함으로 가장 잘 보입니다.

두 가지 방법으로 사용합니다.

  • 참조를 초기화하지 않고 선언하는 것 (나쁜).
  • 선택 사항 (OK)을 나타냅니다.

짐작했듯이 1) 공통 명령형 언어에서 우리에게 끝없는 문제를 일으키는 원인이며 오래 전에 금지 되었어야했으며 2) 진정한 필수 기능입니다.

1) 예방하지 않고 2) 피하는 언어가 있습니다.

예를 들어 OCaml 은 그러한 언어입니다.

1부터 시작하여 계속 증가하는 정수를 반환하는 간단한 함수입니다.

let counter = ref 0;;
let next_counter_value () = (counter := !counter + 1; !counter);;

그리고 선택에 관하여 :

type distributed_computation_result = NotYetAvailable | Result of float;;
let print_result r = match r with
    | Result(f) -> Printf.printf "result is %f\n" f
    | NotYetAvailable -> Printf.printf "result not yet available\n";;

I can't speak to your specific issue, but it sounds like the problem isn't the existence of null. Null exists in databases, you need some way to account for that in the application level. I don't think that's the only reason it exists in .net, mind you. But I figure it's one of the reasons.


I'm surprised no one has talked about databases for their answer. Databases have nullable fields, and any language which will be receiving data from a DB needs to handle that. That means having a null value.

In fact, this is so important that for basic types like int you can make them nullable!

Also consider return values from functions, what if you wanted to have a function divide a couple numbers and the denominator could be 0? The only "correct" answer in such a case would be null. (I know, in such a simple example an exception would likely be a better option... but there can be situations where all values are correct but valid data can produce an invalid or uncalculable answer. Not sure an exception should be used in such cases...)


Besides ALL of the reasons already mentioned, NULL is needed when you need a placeholder for a not-yet created object. For example. if you have a circular reference between a pair of objects, then you need null since you cannot instantiate both simultaneously.

class A {
  B fieldb;
}

class B {
  A fielda;
}

A a = new A() // a.fieldb is null
B b = new B() { fielda = a } // b.fielda isnt
a.fieldb = b // now it isnt null anymore

Edit: You may be able to pull out a language that works without nulls, but it will definitely not be an object oriented language. For example, prolog doesn't have null values.


If you create an object with an instance variable being a reference to some object, what value would you suggest has this variable before you assigned any object reference to it?


I propose:

  1. Ban Null
  2. Extend Booleans: True, False and FileNotFound

Commonly - NullReferenceException means that some method didn't like what it was handed and returned a null reference, which was later used without checking the reference before use.

That method could have thown some more detailed exception instead of returning null, which complies with the fail fast mode of thinking.

Or the method might be returning null as a convenience to you, so that you can write if instead of try and avoid the "overhead" of an exception.


  • Explicit nulling, though that is seldom necessary. Perhaps one can view it as a form of defensive programming.
  • Use it (or nullable(T) structure for non-nullables) as a flag to indicate a missing field when mapping fields from one data source (byte stream, database table) to an object. It can get very unwieldy to make a boolean flag for every possible nullable field, and it may be impossible to use sentinel values like -1 or 0 when all values in the range that field is valid. This is especially handy when there are many many fields.

Whether these are cases of use or abuse is subjective, but I use them sometimes.


Sorry for answering four years late, I am amazed that none of the answers so far have answered the original question this way:

Languages like C# and Java, like C and other languages before them, have null so that the programmer can write fast, optimized code by using pointers in an efficient way.


  • Low-level view

A little history first. The reason why null was invented is for efficiency. When doing low-level programming in assembly, there is no abstraction, you have values in registers and you want to make the most of them. Defining zero to be a not a valid pointer value is an excellent strategy to represent either an object or nothing.

Why waste most of the possible values of a perfectly good word of memory, when you can have a zero-memory-overhead, really fast implementation of the optional value pattern? This is why null is so useful.

  • High-level view.

Semantically, null is in no way necessary to programming languages. For example, in classic functional languages like Haskell or in the ML family, there is no null, but rather types named Maybe or Option. They represent the more high-level concept of optional value without being concerned in any way by what the generated assembly code will look like (that will be the compiler's job).

And this is very useful too, because it enables the compiler to catch more bugs, and that means less NullReferenceExceptions.

  • Bringing them together

In contrast to these very high-level programming languages, C# and Java allow a possible value of null for every reference type (which is another name for type that will end up being implemented using pointers).

This may seem like a bad thing, but what's good about it is that the programmer can use the knowledge of how it works under the hood, to create more efficient code (even though the language has garbage collection).

This is the reason why null still exists in languages nowadays: a trade-off between the need of a general concept of optional value and the ever-present need for efficiency.


The feature that couldn't work without null is being able to represent "the absence of an object".

The absence of an object is an important concept. In object-oriented programming, we need it in order to represent an association between objects that is optional: object A can be attached to an object B, or A might not have an object B. Without null we could still emulate this: for instance we could use a list of objects to associate B with A. That list could contain one element (one B), or be empty. This is somewhat inconvenient and doesn't really solve anything. Code which assumes that there is a B, such as aobj.blist.first().method() is going to blow up in a similar way to a null reference exception: (if blist is empty, what is the behavior of blist.first()?)

Speaking of lists, null lets you terminate a linked list. A ListNode can contain a reference to another ListNode which can be null. The same can be said about other dynamic set structures such as trees. Null lets you have an ordinary binary tree whose leaf nodes are marked by having child references that are null.

Lists and trees can be built without null, but they have to be circular, or else infinite/lazy. That would probably be regarded as an unacceptable constraint by most programmers, who would prefer to have choices in designing data structures.

The pains associated with null references, like null references arising accidentally due to bugs and causing exceptions, are partially a consequence of the static type system, which introduces a null value into every type: there is a null String, null Integer, null Widget, ...

In a dynamically typed language, there can be a single null object, which has its own type. The upshot of this is that you have all the representational advantages of null, plus greater safety. For instance if you write a method which accepts a String parameter, then you're guaranteed that the parameter will be a string object, and not null. There is no null reference in the String class: something that is known to be a String cannot be the object null. References do not have type in a dynamic language. A storage location such as a class member or function parameter contains a value which can be a reference to an object. That object has type, not the reference.

So these languages provide a clean, more or less matematically pure model of "null", and then the static ones turn it into somewhat of a Frankenstein's monster.


If a framework allows the creation of an array of some type without specifying what should be done with the new items, that type must have some default value. For types which implement mutable reference semantics (*) there is in the general case no sensible default value. I consider it a weakness of the .NET framework that there is no way to specify that a non-virtual function call should suppress any null check. This would allow immutable types like String to behave as value types, by returning sensible values for properties like Length.

(*) Note that in VB.NET and C#, mutable reference semantics may be implemented by either class or struct types; a struct type would implement mutable reference semantics by acting as a proxy for a wrapped instance of a class object to which it holds an immutable reference.

It would also be helpful if one could specify that a class should have non-nullable mutable value-type semantics (implying that--at minimum--instantiating a field of that type would create a new object instance using a default constructor, and that copying a field of that type would create a new instance by copying the old one (recursively handling any nested value-type classes).

It's unclear, however, exactly how much support should be built into the framework for this. Having the framework itself recognize the distinctions between mutable value types, mutable reference types, and immutable types, would allow classes which themselves hold references to a mixture of mutable and immutable types from outside classes to efficiently avoid making unnecessary copies of deeply-immutable objects.


Null은 모든 OO 언어의 필수 요구 사항입니다. 개체 참조가 할당되지 않은 개체 변수는 null이어야합니다.

참고 URL : https://stackoverflow.com/questions/178026/why-is-null-present-in-c-sharp-and-java

반응형