DEV Community

VLAD
VLAD

Posted on

Where null Came From, and Why C# Needed 17 Years to Push Back

Object reference not set to an instance of an object. If you write C#, you've read that message more times than you can count. It's a NullReferenceException, and it goes back to a decision one man made in 1965. In 2009 he gave that decision a name: his billion-dollar mistake.

This is the story of where null came from, why it ended up in almost every language you know, and how C# pushed back without breaking existing code. The strange part comes first: the man who added null was trying to make programs safer.

The man who checked everything

Tony Hoare is best known for inventing Quicksort. In his 1980 Turing Award lecture he tells how he started. In August 1960 he became a programmer at Elliott Brothers, a small British computer manufacturer, and was soon asked to design a subset of ALGOL 60 for the company's next machine.

He says the first principle of that design was security. Every correct program should produce either a result or an error message you could understand in terms of your own source code. One consequence: every array subscript was checked at run time against the bounds of the array, every time.

That cost speed. Years later, the company asked its customers whether they wanted an option to switch the checks off for production runs. Hoare says they unanimously asked him not to, because they already knew how often those errors show up in real runs.

So this was someone who put safety before speed.

1965: one value that was easy to add

In 1965, Hoare was working with Niklaus Wirth on a successor to ALGOL 60. The proposal was published in 1966 as "A Contribution to the Development of ALGOL", and the language became known as ALGOL W.

One of its ideas was the record: a bundle of named fields, like a customer with a name and an address. With records came references, variables that point to a record instead of holding it.

Hoare wanted references to be completely safe. Each reference had a type, so a reference to a customer could only point to a customer, and the compiler would check that before the program ran. In the abstract of his 2009 talk he describes it as the first comprehensive type system for references in an object-oriented language.

But programs also need a way to say "nothing here". The last item in a list has no next item. So he added one special value that any reference could hold: null. By his own account, he couldn't resist the temptation, because it was so easy to implement.

What null breaks

The type says Customer. The compiler agrees. But the variable may hold null instead.

Customer c = FindCustomer(id);   // may return null
Console.WriteLine(c.Name);       // NullReferenceException at run time
Enter fullscreen mode Exit fullscreen mode

The type makes a promise the program can't keep. In a safe language, the program stops with an error. In an unsafe one, it reads whatever happens to be in memory. Either way you find out late: not when you write the code, but when a real user hits that one path.

It spread everywhere

ALGOL W never became a big language, but the idea traveled. Wirth went on to design Pascal, which had the same thing under the name nil. C had its null pointer, and so did C++. Java got the famous NullPointerException. And in 2002, C# 1.0 shipped with the same design: null is the default value of every reference type. Declare a field, forget to set it, and it's null.

Two things are worth getting right here.

Myth: Hoare invented the empty value. Lisp already had NIL around 1960. What Hoare says he invented is the null reference: null inside a type system that was built to make references safe.

Myth: null has been shown to cost a billion dollars. Nobody measured it. It was his own rough estimate for forty years of errors, vulnerabilities and crashes, and he qualified it with "probably".

C# looks back

In 2008, Anders Hejlsberg, the lead designer of C#, was asked by Computerworld what he would do differently. He talked about null. His guess was that half of the bugs people run into in C#, and in Java too, are probably null reference exceptions. He wished the type system could say that a parameter may never be null, and have the compiler check it.

The interesting part is that C# had already done this for half of its types. C# 2.0, in 2005, added nullable value types:

int a = null;    // compile error
int? b = null;   // fine: the ? is part of the type
Enter fullscreen mode Exit fullscreen mode

That's the honest design. Null is allowed, but only where the type says so. It just didn't exist for classes.

Other languages went first

Newer languages started from that idea. In Kotlin, a String can never hold null and a String? can. Assigning null to the plain one doesn't compile. Kotlin's documentation on null safety names the billion-dollar mistake as the thing it's designed to reduce.

TypeScript got --strictNullChecks in version 2.0, released in September 2016. Before that, null and undefined were valid values of every type. The lead designer of TypeScript is the same Anders Hejlsberg.

So the idea was proven. The hard part was bringing it to a language with 17 years of existing code.

C# 8: nullable reference types

In November 2017, Mads Torgersen, the C# lead designer, published the plan in "Introducing Nullable Reference Types in C#", quoting Hoare's billion-dollar mistake. The feature shipped in C# 8.0 in September 2019.

With the feature on, the meaning of your existing types changes:

string name = null;          // warning: plain string means "not null"
string? middleName = null;   // fine: this one may be null

Console.WriteLine(middleName.Length);   // warning: possible null dereference
Enter fullscreen mode Exit fullscreen mode

Three design choices made this possible without breaking the world:

  • Warnings, not errors. Old code keeps compiling.
  • Opt-in. At first you had to turn it on yourself. Today, recent .NET project templates set <Nullable>enable</Nullable> for you.
  • No runtime change. string and string? are the same type at run time. Only the compiler sees the difference.

Where null still hides

Null isn't gone from C#. The same documentation lists the places where a non-nullable reference can still hold null with no warning:

string[] names = new string[3];   // every element is null, no warning

struct Student { public string FirstName; }
Student s = default;              // s.FirstName is null, no warning
Enter fullscreen mode Exit fullscreen mode

And there's the null-forgiving operator, !. It tells the compiler "trust me, this isn't null". Every one you write is a place where the compiler stops protecting you.

Why it took 54 years

In 1965, one special value was the easy way to say "nothing here". And a language can't take back its defaults once millions of programs depend on them.

The fix was never to delete null. It was to make the type tell the truth about it. From ALGOL W to C# 8, that took 54 years.

Tony Hoare died on March 5th, 2026, at 92. He gave us Quicksort, and he had the honesty to name his own mistake.

Is nullable turned on in your oldest C# project? And how many warnings did it give you? Tell me in the comments.


I make Vlad's Stack, stories and explainers about how the tools you use every day actually work, for people who write code: https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg

Top comments (0)