Warum ist "null" in C # und Java vorhanden?


73

Wir haben festgestellt, dass viele Fehler in unserer in C # (oder Java) entwickelten Software eine NullReferenceException verursachen.

Gibt es einen Grund, warum "null" überhaupt in die Sprache aufgenommen wurde?

Wenn es keine "Null" gäbe, hätte ich doch keinen Fehler, oder?

Mit anderen Worten, welche Funktion in der Sprache könnte ohne Null nicht funktionieren?


14
-5 Stimmen? Na ja, so viel zu der Idee, dass diese Seite jede Ebene von Programmierfragen begrüßt.
David Arno

8
@ Cody Brocious: Die Community ist eindeutig voll von eher unangenehmen Leuten, die diejenigen bestrafen, die es wagen, extreme Fragen an Neulinge zu stellen. Die Antwort mag für Sie offensichtlich sein, wird aber für diejenigen, die gerade erst mit der Programmierung beginnen, nicht offensichtlich sein
David Arno

2
Und es ist definitiv keine Anfängerfrage! Null ist ein Relikt von Assembly / C, kein wesentliches Merkmal!
Bltxd

5
Ich kann nicht verstehen, wie diese Frage so viele positive Stimmen bekommen hat.
Konrad Rudolph

8
qconlondon.com/london-2009/presentation/… - Tony Hoare, Erfinder von NULL (als schneller Hack in Algol), hielt einen Vortrag darüber, warum dies eine schlechte Idee ist. Wenn Turing preisgekrönte Halbgötter dies in Frage stellen können, warum sagen die Leute dann, dass die Antwort offensichtlich ist?
Pete Kirkham

Antworten:


91

Anders Hejlsberg, "C # Vater", hat gerade in seinem Computerworld-Interview über diesen Punkt gesprochen :

Zum Beispiel haben wir im Typsystem keine Trennung zwischen Wert- und Referenztypen und keine Nullfähigkeit von Typen. Dies mag ein wenig wackelig oder ein wenig technisch klingen, aber in C # können Referenztypen null sein, z. B. Zeichenfolgen, aber Werttypen können nicht null sein. Es wäre sicher schön, nicht nullfähige Referenztypen zu haben, also könnten Sie deklarieren, dass 'diese Zeichenfolge niemals null sein kann und ich möchte, dass Sie vom Compiler überprüfen, ob ich hier niemals einen Nullzeiger treffen kann'.

50% der Fehler, auf die Menschen heute stoßen, wenn sie auf unserer Plattform mit C # codieren, und das Gleiche gilt für Java, sind wahrscheinlich Nullreferenzausnahmen. Wenn wir ein stärkeres Typsystem gehabt hätten, mit dem Sie sagen könnten, dass dieser Parameter niemals null sein kann, und Sie als Compiler überprüfen Sie dies bitte bei jedem Aufruf, indem Sie eine statische Analyse des Codes durchführen. Dann hätten wir Klassen von Fehlern ausrotten können.

Cyrus Najmabadi, ein ehemaliger Software-Design-Ingenieur im C # -Team (der jetzt bei Google arbeitet), diskutiert dieses Thema in seinem Blog: ( 1. , 2. , 3. , 4. ). Es scheint, dass das größte Hindernis für die Einführung von nicht nullbaren Typen darin besteht, dass die Notation die Gewohnheiten und die Codebasis der Programmierer stören würde. Etwa 70% der Referenzen von C # -Programmen werden wahrscheinlich als nicht nullfähige Programme enden.

Wenn Sie wirklich einen nicht nullbaren Referenztyp in C # haben möchten, sollten Sie versuchen, Spec # zu verwenden , eine C # -Erweiterung, die die Verwendung von "!" als nicht nullbares Zeichen.

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

1
Seltsamerweise habe ich erst gestern Abend über einen schmutzigen Hack gebloggt , um etwas davon zu implementieren: msmvps.com/blogs/jon_skeet/archive/2008/10/06/… Es ist wirklich keine schöne Lösung mit verschiedenen Fallstricken, aber dennoch interessant ( IMO!).
Jon Skeet

12
Eigentlich sollten alle Typen implizit nicht nullwertfähig sein, auch Referenztypen. Nullable Typen (Werttypen sowie Referenztypen) sollten explizit mit "?" Markiert werden.
Michael Damatov

5
Kleine Korrektur: Cyrus Najmabadi ist nicht mehr im C # -Team. Ich habe das nicht bemerkt, bis ich heute eine interne E-Mail von ihm bekommen habe, hier bei Google :)
Jon Skeet

29

Die Nichtigkeit ist eine natürliche Folge von Referenztypen. Wenn Sie eine Referenz haben, muss diese auf ein Objekt verweisen - oder null sein. Wenn Sie die Nichtigkeit verbieten würden, müssten Sie immer sicherstellen, dass jede Variable mit einem Nicht-Null-Ausdruck initialisiert wurde - und selbst dann hätten Sie Probleme, wenn Variablen während der Initialisierungsphase gelesen würden.

Wie würden Sie vorschlagen, das Konzept der Nichtigkeit zu entfernen?


4
Es ist nicht grundlegend. Sie können verlangen, dass jede Variable vom Typ ref wie bei (nicht nullbaren) Werttypen initialisiert wird. Es wäre allerdings ein Ärger.
JacquesB

1
Angenommen, Sie benötigen eine initialisierte Variable vom Typ ref. Angenommen, dies ist ein statisches Feld, und Sie rufen eine statische Methode auf, um das Feld zu initialisieren. Was passiert, wenn diese Methode ein anderes Feld betrachtet, das noch nicht initialisiert wurde? Welchen Wert hätte es?
Jon Skeet

2
Kurz gesagt: Es gibt wahrscheinlich Möglichkeiten, dies zu umgehen, aber sie würden zunehmend labyrinthisch werden, wenn Sie sich Randfälle ansehen.
Jon Skeet

10
In der reinen Funktionsprogrammierung können Werte nur über Funktionsparameter und Ausdrucksauswertungen (einschließlich Funktionsaufrufe) angegeben werden, die alle vor ihrer Verwendung angegeben werden müssen und nur für den von ihnen definierten Bereich existieren, sofern sie nicht in einem Abschluss erfasst werden.
Mark Cidade

3
@ Jon, es gibt 2 Konzepte unter dem Namen "nullity": nicht initialisiert und optional. Dies ist es, was es zu einem Übertragungsfehler von Algol macht.
Bltxd

20

Wie viele Dinge in der objektorientierten Programmierung geht alles auf ALGOL zurück. Tony Hoare nannte es einfach seinen "Milliarden-Dollar-Fehler". Wenn überhaupt, ist das eine Untertreibung.

Hier ist eine wirklich interessante These, wie man die Nullfähigkeit in Java nicht zum Standard macht. Die Parallelen zu C # sind offensichtlich.


Kotlin ist null sicher. Es ist eher eine Design-Wahl der Sprache, die Sie dazu zwingt, sicherzustellen, dass Ihre Refs (oder Ptrs) auf etwas verweisen.
Anis

17

Null in C # ist meistens eine Übertragung von C ++, die Zeiger hatte, die auf nichts im Speicher (oder besser gesagt auf eine Adresse 0x00) zeigten . In diesem Interview sagt Anders Hejlsberg, dass er nicht nullbare Referenztypen in C # hinzugefügt hätte.

Null hat jedoch auch einen legitimen Platz in einem Typsystem, ähnlich dem unteren Typ (wo objectist der obere Typ). In lisp ist der untere Typ NILund in Scala ist es Nothing.

Es wäre schon möglich gewesen C # ohne NULL - Werte zu entwerfen , aber dann würden Sie mit einer akzeptablen Lösung für die Verwendungen haben zu kommen , dass die Menschen in der Regel für haben null, wie unitialized-value, not-found, default-value, undefined-value, und None<T>. Es hätte wahrscheinlich weniger Akzeptanz bei C ++ - und Java-Programmierern gegeben, wenn ihnen dies trotzdem gelungen wäre. Zumindest bis sie sahen, dass C # -Programme niemals Nullzeigerausnahmen hatten.


4
Ganz zu schweigen davon, dass Sie nur null umbenennen würden.
CodeRedick

1
Sie müssen das Schlüsselwort "null" nicht durch ein anderes einzelnes Schlüsselwort ersetzen. Sie können einschränken, was "null" bedeutet, und dann andere Schlüsselwörter hinzufügen, um andere Bedeutungen zu haben. Dies kann Nullreferenzfehler reduzieren. Sogar JavaScript hat zusätzlich zu "null" "undefiniert". VB6 hatte ein "Leer", "Nichts" UND "Null"
Mark Cidade

Abgestimmt. Sie sind verwirrt zwischen den beiden Verwendungen von null: "nicht initialisiert" ist nutzlos (um jalf zu zitieren "bedeutungslose Zustände sollten nicht darstellbar sein") und unterscheidet sich von der erforderlichen "Optionalität".
Bltxd

@bltxd: ... was? Könnten Sie bitte
näher darauf eingehen

2
Ich bin nur erstaunt, dass einige Leute (siehe Telos oben) immer noch None und Null verwechseln. Ich stimme dem zweiten Absatz größtenteils nicht zu: Null hat KEINEN legitimen Platz in einem modernen Typsystem. Null ist ein einzelner Wert, der mit jedem Referenztyp kompatibel ist. Keine <T> ist dies nicht, da er parametrisch ist. Das ist ein großer Unterschied. Die Abwertung ist vielleicht etwas hart, aber das Poster scheint zu implizieren, dass einige Leute einen "uninitialisierten Wert" verwenden, was falsch ist.
Bltxd

13

Das Entfernen von Null würde nicht viel lösen. Für die meisten Variablen, die auf init festgelegt sind, benötigen Sie eine Standardreferenz. Anstelle von Nullreferenzausnahmen würden Sie unerwartetes Verhalten erhalten, da die Variable auf die falschen Objekte zeigt. Zumindest Nullreferenzen schlagen schnell fehl, anstatt unerwartetes Verhalten zu verursachen.

Sie können sich das Nullobjektmuster ansehen, um einen Teil dieses Problems zu lösen


3
Ich nenne das Unsinn. Durch die Einführung eines nicht nullbaren Referenztyps können Sie auswählen, welchen Sie benötigen. OCaml und Haskell haben nicht nullbare Referenzen und sind dennoch auf alle Problemdomänen anwendbar und leiden überhaupt nicht unter NullReferenceException ...
bltxd

OCaml und Haskell unterscheiden sich aufgrund unterschiedlicher Ideen grundlegend von C # und Java. Wenn Sie nur null aus C # oder Java entfernen, verhalten sich diese Sprachen nicht mehr wie eine reine Funktionssprache wie Haskell.
Mendelt

12

Null ist eine äußerst leistungsstarke Funktion. Was machen Sie, wenn Sie keinen Wert haben? Es ist NULL!

Eine Denkrichtung ist, niemals null zurückzugeben, eine andere ist immer. Einige sagen beispielsweise, Sie sollten ein gültiges, aber leeres Objekt zurückgeben.

Ich bevorzuge null, da es für mich ein wahrer Hinweis darauf ist, was es tatsächlich ist. Wenn ich eine Entität nicht aus meiner Persistenzschicht abrufen kann, möchte ich null. Ich möchte keinen leeren Wert. Aber das bin ich.

Es ist besonders praktisch bei Grundelementen. Zum Beispiel, wenn ich wahr oder falsch habe, es aber in einem Sicherheitsformular verwendet wird, in dem eine Berechtigung Zulassen, Verweigern oder Nicht festgelegt sein kann. Nun, ich möchte, dass das nicht null ist. Also kann ich bool benutzen?

Es gibt noch viel mehr, worüber ich weitermachen könnte, aber ich werde es dort lassen.


2
Was Sie in der Situation Zulassen / Verweigern / NotSet wollen, ist eine Aufzählung, nicht etwas, das null enthält.
Mark Cidade

2
False, True, FileNotFound. :-)
Chris Jester-Young

Zulassen = Wahr, Verweigern = Falsch, Nicht gesetzt = Kein Wert, daher NULL. Das war ein einfaches Beispiel. Ja, Nein, Keine Daten, Richtig Falsch Unentschlossen usw. Die Liste kann immer weitergehen. Sie passt perfekt zu nullbarem Bool und nicht zu einer Aufzählung. Wenn ich Allow / Deny / IDontKNow / WhoCares / IAmHungry / NotSet hätte, dann würde ich enum verwenden.
Mattlant

Sie werden feststellen, dass null möglicherweise der schlechteste Weg ist, ein Element ohne Informationen in Daten zu integrieren.
Ich gebe Crap Antworten

1
Was ist im Beispiel, wenn der Code einen Fehler enthält und der Programmierer nur vergessen hat, dem Flag Zulassen / Verweigern einen Wert zuzuweisen? In diesem Fall ist der Wert null ein Fehler und bedeutet nicht 'NOT SET'.
Outlaw Programmer

12

Wenn es keine "Null" gäbe, hätte ich doch keinen Fehler, oder?

Die Antwort lautet NEIN . Das Problem ist nicht, dass C # null zulässt, das Problem ist, dass Sie Fehler haben, die sich zufällig mit der NullReferenceException manifestieren. Wie bereits erwähnt, haben Nullen in der Sprache den Zweck, entweder einen "leeren" Referenztyp oder einen Nichtwert (leer / nichts / unbekannt) anzugeben.


3
Das Entfernen von Null aus der Sprache würde die Wahrscheinlichkeit bestimmter Fehlerklassen auf dieselbe Weise verringern wie das Entfernen von Zeigertypen die Wahrscheinlichkeit bestimmter Fehlerklassen. Wenn Sie wirklich null benötigen, können Sie einen unsicheren Block verwenden. Werttypen existieren einwandfrei, ohne nullwertfähig zu sein.
Mark Cidade

2
Der Punkt, den pro3carp3 anstrebt, ist, dass die meisten Null-Fehler mit der Verwendung nicht initialisierter Variablen zusammenhängen, als wären sie für die Verwendung vorbereitet. Nicht nullfähige Typen lösen wirklich nur den Fall "Es ist mir egal, welchen Wert ich habe, ich möchte nur diese Methode ausführen", während Sie sich normalerweise darum kümmern.
Guvante

Abgestimmt. Ihre Prämisse ist, dass Sie Nullen benötigen, um nicht initialisierte Refs darzustellen. Dies gilt, wenn Sie wirklich darauf bestehen, nicht initialisierte Refs in Ihrer Sprache zuzulassen. Die eigentliche Frage ist jedoch, ob dies Sinn macht und die Antwort eindeutig Nein lautet. Dies entspricht der Anforderung, ungültige Programmzustände darstellen zu können.
Bltxd

bltxd: Ihr Kommentar repräsentiert eine naive Sichtweise. Ungültige Programmzustände treten aus Gründen auf, die außerhalb der Kontrolle des Programmierers liegen, z. B. Dateisystemfehler, Probleme mit der Internetverbindung usw. In diesen Situationen ist es sinnvoll, Nullen zu verwenden. Es ist auch von unschätzbarem Wert für die Darstellung unbekannter, ungültiger und leerer Datenbankfelder.
Pro3Carp3

Bitte werfen Sie einen Blick auf OCaml- oder Haskell-Bindungen für PostgreSQL. Sie verwalten alles, was Sie sagen, ohne eine allgegenwärtige Nullreferenz. Kurz gesagt, die bestehende Praxis beweist, dass Sie falsch liegen.
Bltxd

5

Null verursacht keine NullPointerExceptions ...

Programmierer verursachen NullPointerExceptions.

Ohne Nullen verwenden wir wieder einen tatsächlichen beliebigen Wert, um festzustellen, dass der Rückgabewert einer Funktion oder Methode ungültig war. Sie müssen immer noch nach dem zurückgegebenen -1 (oder was auch immer) suchen. Das Entfernen von Nullen löst die Faulheit nicht auf magische Weise, sondern verschleiert sie mürrisch.


Abgestimmt. Probieren Sie eine Sprache der ML-Familie aus und lassen Sie sich aufklären.
Bltxd

Ich habe gerade die Abwertung bemerkt ... obwohl es Kinna ist, die den Punkt der Frage zunächst verfehlt, da sie speziell auf C # und Java ausgerichtet war. Das heißt, ich werde sie überprüfen und möchte nicht den Schlüssel zum Himmel verpassen!
Newtopian

5

Die Frage kann interpretiert werden als "Ist es besser, einen Standardwert für jeden Referenztyp (wie String.Empty) oder null zu haben?". In dieser Perspektive würde ich es vorziehen, Nullen zu haben, weil;

  • Ich möchte nicht für jede Klasse, die ich schreibe, einen Standardkonstruktor schreiben.
  • Ich möchte nicht, dass unnötiger Speicher für solche Standardwerte zugewiesen wird.
  • Die Überprüfung, ob eine Referenz null ist, ist billiger als Wertvergleiche.
  • Es ist sehr wahrscheinlich, dass anstelle von NullReferanceExceptions mehr Fehler auftreten, die schwerer zu erkennen sind. Es ist gut, eine solche Ausnahme zu haben, die deutlich anzeigt, dass ich etwas falsch mache (vorausgesetzt).

3

"Null" ist in der Sprache enthalten, da wir Werttypen und Referenztypen haben. Es ist wahrscheinlich ein Nebeneffekt, aber ein guter, denke ich. Es gibt uns viel Macht darüber, wie wir den Speicher effektiv verwalten.

Warum haben wir null? ...

Werttypen werden auf dem "Stapel" gespeichert, ihr Wert befindet sich direkt in diesem Speicher (dh int x = 5 bedeutet, dass der Speicherort für diese Variable "5" enthält).

Referenztypen hingegen haben einen "Zeiger" auf dem Stapel, der auf den tatsächlichen Wert auf dem Heap zeigt (dh Zeichenfolge x = "ello" bedeutet, dass der Speicherblock auf dem Stapel nur eine Adresse enthält, die auf den tatsächlichen Wert auf dem Heap zeigt ).

Ein Nullwert bedeutet einfach, dass unser Wert auf dem Stapel nicht auf einen tatsächlichen Wert auf dem Heap verweist - es ist ein leerer Zeiger.

Hoffe ich habe das gut genug erklärt.


Abgestimmt. Siehe Optionstypen in Sprachen der ML-Familie. Kurz gesagt, indem Sie Referenzen nicht nullbar machen, verlieren Sie keine Allgemeinheit, sondern machen Ihre Programme einfach NullReferenceException frei.
Bltxd

3

Wenn Sie eine 'NullReferenceException' erhalten, verweisen Sie möglicherweise weiterhin auf Objekte, die nicht mehr vorhanden sind. Dies ist kein Problem mit 'null', sondern ein Problem mit Ihrem Code, der auf nicht vorhandene Adressen verweist.


3

Es gibt Situationen, in denen null ein guter Weg ist, um anzuzeigen, dass eine Referenz nicht initialisiert wurde. Dies ist in einigen Szenarien wichtig.

Zum Beispiel:

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

Dies wird in den meisten Fällen durch die Verwendung einer using- Anweisung erreicht. Aber das Muster ist immer noch weit verbreitet.

In Bezug auf Ihre NullReferenceException lässt sich die Ursache solcher Fehler häufig leicht reduzieren, indem ein Codierungsstandard implementiert wird, bei dem alle Parameter auf ihre Gültigkeit überprüft werden. Abhängig von der Art des Projekts finde ich, dass es in den meisten Fällen ausreicht, Parameter für exponierte Mitglieder zu überprüfen. Wenn die Parameter nicht innerhalb des erwarteten Bereichs liegen, wird abhängig vom verwendeten Fehlerbehandlungsmuster eine ArgumentException ausgelöst oder ein Fehlerergebnis zurückgegeben.

Die Parameterprüfung entfernt an sich keine Fehler, aber auftretende Fehler sind während der Testphase leichter zu finden und zu korrigieren.

Als Hinweis hat Anders Hejlsberg das Fehlen einer Nicht-Null-Durchsetzung als einen der größten Fehler in der C # 1.0-Spezifikation erwähnt, und dass es "schwierig" ist, es jetzt aufzunehmen.

Wenn Sie immer noch der Meinung sind, dass ein statisch erzwungener Referenzwert ungleich Null von großer Bedeutung ist, können Sie die Spezifikationssprache überprüfen . Es ist eine Erweiterung von C #, bei der Nicht-Null-Referenzen Teil der Sprache sind. Dies stellt sicher, dass einer als nicht null gekennzeichneten Referenz niemals eine Nullreferenz zugewiesen werden kann.


2

In einer Antwort wurde erwähnt, dass Datenbanken Nullen enthalten. Das stimmt, aber sie unterscheiden sich sehr von Nullen in C #.

In C # sind Nullen Markierungen für eine Referenz, die sich auf nichts bezieht.

In Datenbanken sind Nullen Marker für Wertzellen, die keinen Wert enthalten. Mit Wertzellen meine ich im Allgemeinen den Schnittpunkt einer Zeile und einer Spalte in einer Tabelle, aber das Konzept der Wertzellen könnte über Tabellen hinaus erweitert werden.

Der Unterschied zwischen den beiden scheint auf den ersten Blick trivial. Aber es ist nicht.


1 die Differenz zwischen null für den Hinweis Referenzen und Werte etwas bedeuten , ist null / ungültig / default / leer / was auch immer.
R. Martinho Fernandes

2

Null, wie es in C # / C ++ / Java / Ruby verfügbar ist, wird am besten als eine Kuriosität einer obskuren Vergangenheit (Algol) angesehen, die irgendwie bis heute überlebt hat.

Sie verwenden es auf zwei Arten:

  • Referenzen deklarieren, ohne sie zu initialisieren (schlecht).
  • Optionalität kennzeichnen (OK).

Wie Sie vermutet haben, ist 1) das, was uns endlose Probleme in gängigen imperativen Sprachen verursacht und vor langer Zeit hätte verboten werden müssen. 2) ist das wahre wesentliche Merkmal.

Es gibt Sprachen, die 1) vermeiden, ohne 2) zu verhindern.

Zum Beispiel ist OCaml eine solche Sprache.

Eine einfache Funktion, die eine immer höher werdende Ganzzahl ab 1 zurückgibt:

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

Und in Bezug auf die Optionalität:

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";;

1
1 ist schlecht? Immer, sagst du? Was ist mit der Ausnahmebehandlung, wenn Sie den Wert einer Variablen im catch-Block sehen möchten? Es ist ziemlich notwendig, es außerhalb des Versuchs zu deklarieren und innerhalb zu initialisieren, wenn die Initialisierung die Ausnahme verursachen könnte!
CodeRedick

Wenn Ihre Variablenkonstruktion fehlgeschlagen ist, ist ihr Wert null.
Bltxd

Zumindest in Java müssen Sie Referenzen deklarieren, ohne sie zu initialisieren: nicht primitive Klassenfelder (im Konstruktor initialisiert), Objekte, die beim Erstellen eine Ausnahme auslösen, die Sie außerhalb des entsprechenden try / catch-Blocks verwenden müssen usw.
PhiLho

@ blue - Und wenn es keinen "Null" -Wert gäbe, gäbe es keine Möglichkeit, einen Fehler anzuzeigen? Denn ohne einen Nullwert wäre die Variable Müll und es ist nicht immer möglich, einen Nicht-Null-Müllwert von einem guten Wert zu unterscheiden.
Onorio Catenacci

@PhiLho: Ein Objekt, das zur Erstellungszeit eine Ausnahme ausgelöst hat, wird niemals erstellt. Um es später zu verwenden, müssen Sie natürlich null verwenden, aber Sie können die erfolgreiche Initialisierung auch mit einem Booleschen Wert verfolgen. Wie im Wesentlichen erfordert Ihre Logik keine Null, sondern nutzt sie.
Bltxd

1

Ich kann nicht mit Ihrem speziellen Problem sprechen, aber es scheint, dass das Problem nicht die Existenz von Null ist. In Datenbanken ist Null vorhanden. Sie benötigen eine Möglichkeit, dies auf Anwendungsebene zu berücksichtigen. Ich glaube nicht, dass dies der einzige Grund ist, warum es in .net existiert, wohlgemerkt. Aber ich denke, das ist einer der Gründe.


-1. In .NET wird eine Datenbank NULLnormalerweise als DBNull.Valueanstelle von dargestellt null.
stakx - nicht mehr beitragen

1

Ich bin überrascht, dass niemand über Datenbanken für ihre Antwort gesprochen hat. Datenbanken haben nullfähige Felder, und jede Sprache, die Daten von einer Datenbank empfängt, muss damit umgehen. Das bedeutet, einen Nullwert zu haben.

Tatsächlich ist dies so wichtig, dass Sie Basistypen wie int auf Null setzen können!

Berücksichtigen Sie auch Rückgabewerte von Funktionen. Was wäre, wenn eine Funktion ein paar Zahlen teilen soll und der Nenner 0 sein könnte? Die einzige "richtige" Antwort in einem solchen Fall wäre null. (Ich weiß, in einem so einfachen Beispiel wäre eine Ausnahme wahrscheinlich die bessere Option ... aber es kann Situationen geben, in denen alle Werte korrekt sind, aber gültige Daten zu einer ungültigen oder nicht kalkulierbaren Antwort führen können. Ich bin mir nicht sicher, ob in solchen Fällen eine Ausnahme verwendet werden sollte Fälle...)


2
DB-Zeug zu behandeln bedeutet, Nullwerte zu haben . Der große Unterschied besteht darin, dass Sie in C # keine Referenzen haben . Es gibt bereits ein Teilungsbeispiel, in dem Sie keine Ausnahme auslösen (IEEE 754-Gleitkomma-Arithmetik), und es beweist, dass die "einzige" richtige Antwort keine Nullreferenzen sind. IEEE 754 verwendet einen Nullwert (NaN - Not a Number).
R. Martinho Fernandes

1

Neben ALLEN bereits erwähnten Gründen wird NULL benötigt, wenn Sie einen Platzhalter für ein noch nicht erstelltes Objekt benötigen. Zum Beispiel. Wenn Sie einen Zirkelverweis zwischen zwei Objekten haben, benötigen Sie null, da Sie nicht beide gleichzeitig instanziieren können.

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

Bearbeiten: Möglicherweise können Sie eine Sprache herausziehen, die ohne Nullen funktioniert, aber es wird definitiv keine objektorientierte Sprache sein. Zum Beispiel hat Prolog keine Nullwerte.


1
Wählen Sie eine vorhandene OO-Sprache wie C #. Jetzt müssen Sie bei der Deklaration / Konstruktion alles initialisieren und das Null-Objektmuster für alles verwenden, was Sie für null / undefiniert / ungültig / standardmäßig / leer / was auch immer benötigen.
R. Martinho Fernandes

0

Wenn Sie ein Objekt mit einer Instanzvariablen erstellen, die auf ein Objekt verweist, welchen Wert haben Sie für diese Variable, bevor Sie ihr eine Objektreferenz zugewiesen haben?


1
Viele Möglichkeiten. Beispielsweise könnte der Compiler erzwingen, dass Variablen nicht verwendet werden können, bevor sie explizit initialisiert wurden. Oder der Compiler könnte einen Standardkonstruktor (ohne Parameter) verwenden und einen Fehler ausgeben, wenn kein solcher Konstruktor gefunden wird (wie in C ++)
Niki

0

Ich schlage vor:

  1. Ban Null
  2. Erweitern Sie Boolesche Werte: True, False und FileNotFound

2
Leider glaube ich nicht, dass die meisten Leute den Witz verstehen werden.
rjzii

0

Häufig - NullReferenceException bedeutet, dass einigen Methoden die Übergabe nicht gefallen hat und eine Nullreferenz zurückgegeben wurde, die später verwendet wurde, ohne die Referenz vor der Verwendung zu überprüfen.

Diese Methode hätte eine detailliertere Ausnahme auslösen können, anstatt null zurückzugeben, was der ausfallsicheren Denkweise entspricht.

Oder die Methode gibt aus praktischen Gründen möglicherweise null zurück, sodass Sie schreiben können, wenn Sie nicht versuchen , den "Overhead" einer Ausnahme zu vermeiden.


0
  • Explizites Nullen, obwohl dies selten notwendig ist. Vielleicht kann man es als eine Form der defensiven Programmierung betrachten.
  • Verwenden Sie es (oder die Nullable (T) -Struktur für Nicht-Nullables) als Flag, um ein fehlendes Feld anzuzeigen, wenn Felder von einer Datenquelle (Bytestream, Datenbanktabelle) einem Objekt zugeordnet werden. Es kann sehr unhandlich werden, für jedes mögliche nullbare Feld ein Boolesches Flag zu erstellen, und es kann unmöglich sein, Sentinel-Werte wie -1 oder 0 zu verwenden, wenn alle Werte in dem Bereich dieses Felds gültig sind. Dies ist besonders praktisch, wenn es viele, viele Felder gibt.

Ob dies Fälle von Gebrauch oder Missbrauch sind, ist subjektiv, aber ich benutze sie manchmal.


0

Es tut mir leid, dass ich vier Jahre zu spät geantwortet habe. Ich bin erstaunt, dass bisher keine der Antworten die ursprüngliche Frage folgendermaßen beantwortet hat:

Sprachen wie C # und Java , wie C und andere Sprachen vor ihnen, habennull es dem Programmierer ermöglicht , schnellen, optimierten Code zu schreiben, indem er Zeiger auf effiziente Weise verwendet.


  • Low-Level-Ansicht

Zuerst ein bisschen Geschichte. Der Grund, warum nullerfunden wurde, ist für die Effizienz. Wenn Sie in Assembly auf niedriger Ebene programmieren, gibt es keine Abstraktion, Sie haben Werte in Registern und Sie möchten das Beste daraus machen. Die Definition von Null als ungültigen Zeigerwert ist eine hervorragende Strategie, um entweder ein Objekt oder nichts darzustellen .

Warum sollten Sie die meisten möglichen Werte eines einwandfreien Speicherworts verschwenden, wenn Sie das optionale Wertemuster schnell und ohne Speicheraufwand implementieren können ? Deshalb nullist es so nützlich.

  • Übergeordnete Ansicht.

Semantisch nullist es für Programmiersprachen in keiner Weise notwendig. In klassischen Funktionssprachen wie Haskell oder in der ML-Familie gibt es beispielsweise keine Null, sondern Typen mit dem Namen "Vielleicht" oder "Option". Sie stellen das übergeordnete Konzept des optionalen Werts dar, ohne sich in irgendeiner Weise darum zu kümmern, wie der generierte Assemblycode aussehen wird (das ist die Aufgabe des Compilers).

Dies ist auch sehr nützlich, da der Compiler dadurch mehr Fehler abfangen kann und dies weniger NullReferenceExceptions bedeutet.

  • Bring sie zusammen

Im Gegensatz zu diesen Programmiersprachen auf sehr hoher Ebene erlauben C # und Java für jeden Referenztyp einen möglichen Wert von null (dies ist ein anderer Name für den Typ, der am Ende mithilfe von Zeigern implementiert wird ).

Dies mag schlecht erscheinen, aber das Gute daran ist, dass der Programmierer das Wissen darüber, wie es unter der Haube funktioniert, nutzen kann, um effizienteren Code zu erstellen (obwohl die Sprache über eine Garbage Collection verfügt).

Dies ist der Grund, warum nulles heutzutage immer noch Sprachen gibt: ein Kompromiss zwischen der Notwendigkeit eines allgemeinen Konzepts des optionalen Werts und dem allgegenwärtigen Bedürfnis nach Effizienz.


0

Die Funktion, die ohne null nicht funktionieren könnte, kann "das Fehlen eines Objekts" darstellen.

Das Fehlen eines Objekts ist ein wichtiges Konzept. In der objektorientierten Programmierung benötigen wir sie, um eine optionale Zuordnung zwischen Objekten darzustellen: Objekt A kann an ein Objekt B angehängt werden, oder A hat möglicherweise kein Objekt B. Ohne Null könnten wir dies dennoch emulieren: zum Beispiel Wir könnten eine Liste von Objekten verwenden, um B mit A zu verknüpfen. Diese Liste könnte ein Element (ein B) enthalten oder leer sein. Dies ist etwas unpraktisch und löst nichts wirklich. Code, der davon ausgeht, dass es ein B gibt, wie aobj.blist.first().method()es in ähnlicher Weise wie eine Nullreferenzausnahme explodiert: (Wenn blistes leer ist, wie verhält es sich blist.first()?)

Apropos Listen: Mit null können Sie eine verknüpfte Liste beenden. A ListNodekann einen Verweis auf einen anderen enthalten, ListNodeder null sein kann. Gleiches gilt für andere dynamische Mengenstrukturen wie Bäume. Mit Null können Sie einen normalen Binärbaum haben, dessen Blattknoten durch untergeordnete Referenzen gekennzeichnet sind, die null sind.

Listen und Bäume können ohne Null erstellt werden, müssen jedoch kreisförmig oder unendlich / faul sein. Dies wird wahrscheinlich von den meisten Programmierern als inakzeptable Einschränkung angesehen, die es vorziehen würden, beim Entwerfen von Datenstrukturen die Wahl zu haben.

Die mit Nullreferenzen verbundenen Schmerzen, wie Nullreferenzen, die versehentlich aufgrund von Fehlern auftreten und Ausnahmen verursachen, sind teilweise eine Folge des statischen Typsystems, das in jeden Typ einen Nullwert einführt: Es gibt einen Null-String, eine Null-Ganzzahl, ein Null-Widget, ...

In einer dynamisch typisierten Sprache kann es ein einzelnes Nullobjekt geben, das einen eigenen Typ hat. Das Ergebnis ist, dass Sie alle Darstellungsvorteile von Null sowie mehr Sicherheit haben. Wenn Sie beispielsweise eine Methode schreiben, die einen String-Parameter akzeptiert, ist garantiert, dass der Parameter ein String-Objekt und nicht null ist. In der String-Klasse gibt es keine Nullreferenz: Etwas, von dem bekannt ist, dass es ein String ist, kann nicht das Objekt null sein. Referenzen haben keinen Typ in einer dynamischen Sprache. Ein Speicherort wie ein Klassenmitglied oder ein Funktionsparameter enthält einen Wert, der eine Referenz auf ein Objekt sein kann. Dieses Objekt hat den Typ, nicht die Referenz.

Diese Sprachen bieten also ein sauberes, mehr oder weniger matematisch reines Modell von "Null", und die statischen verwandeln es dann in ein Frankenstein-Monster.


0

Wenn ein Framework die Erstellung eines Arrays eines bestimmten Typs ermöglicht, ohne anzugeben, was mit den neuen Elementen geschehen soll, muss dieser Typ einen Standardwert haben. Für Typen, die eine veränderbare Referenzsemantik (*) implementieren, gibt es im Allgemeinen keinen sinnvollen Standardwert. Ich halte es für eine Schwäche des .NET-Frameworks, dass es keine Möglichkeit gibt, anzugeben, dass ein nicht virtueller Funktionsaufruf eine Nullprüfung unterdrücken soll. Dies würde es unveränderlichen Typen wie String ermöglichen, sich als Werttypen zu verhalten, indem sinnvolle Werte für Eigenschaften wie Länge zurückgegeben werden.

(*) Beachten Sie, dass in VB.NET und C # veränderbare Referenzsemantiken entweder durch Klassen- oder Strukturtypen implementiert werden können. Ein Strukturtyp würde eine veränderbare Referenzsemantik implementieren, indem er als Proxy für eine umschlossene Instanz eines Klassenobjekts fungiert, auf das er eine unveränderliche Referenz enthält.

Es wäre auch hilfreich, wenn man angeben könnte, dass eine Klasse eine nicht nullbare veränderbare Semantik vom Werttyp haben soll (was bedeutet, dass durch das Instanziieren eines Feldes dieses Typs zumindest eine neue Objektinstanz unter Verwendung eines Standardkonstruktors erstellt wird, und dass Durch Kopieren eines Felds dieses Typs wird eine neue Instanz erstellt, indem die alte kopiert wird (rekursives Behandeln verschachtelter Werttypklassen).

Es ist jedoch unklar, wie viel Unterstützung genau in den Rahmen dafür eingebaut werden sollte. Wenn das Framework selbst die Unterschiede zwischen veränderlichen Werttypen, veränderlichen Referenztypen und unveränderlichen Typen erkennt, können Klassen, die selbst Verweise auf eine Mischung aus veränderlichen und unveränderlichen Typen von externen Klassen enthalten, effizient vermeiden, unnötige Kopien tief unveränderlicher Objekte zu erstellen.


-3

Null ist eine wesentliche Voraussetzung für jede OO-Sprache. Jede Objektvariable, der keine Objektreferenz zugewiesen wurde, muss null sein.


Also nikie, möchtest du diesem unwissenden alten Narren erklären, was mit meiner Aussage falsch ist?
David Arno

@ David: Grundsätzlich sind Nullreferenzen keine wesentliche Voraussetzung für eine OO-Sprache. Eine Lösung: Sie müssen bei der Deklaration / Konstruktion alles initialisieren und das Nullobjektmuster für alles verwenden, was Sie für null / undefiniert / ungültig / standardmäßig / leer / was auch immer benötigen.
R. Martinho Fernandes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.