Dataslope logoDataslope

The Type System

How C# uses types to catch bugs at compile time, and the crucial difference between value types and reference types

We've been using the word type for a while now. This page is the one where we slow down and look at it properly. C# is what's called a statically typed language, and the type system is one of the biggest reasons C# programs are easier to maintain than programs in dynamically typed languages.

Static typing in one sentence

In C#, every variable, parameter, field, and return value has a type that is known at compile time, and the compiler refuses to compile any code that uses those values in a way that doesn't match.

That's it. The compiler is doing a constant check: does this code make sense given the types?

Code Block
C# 13

Static typing is not glamorous. But the moment you have a program of more than a few hundred lines, it becomes the difference between "works the first time" and "fails in production at 3 AM."

What a type really is

A type is a promise about what a value can do. When you say int age = 30;, you're promising:

  • This box holds a 32-bit integer.
  • You can add it, compare it, increment it.
  • You cannot call .Substring(0, 3) on it (that's a string operation).

The compiler reads those promises and enforces them. If you try to call .Substring on age, you don't find out at 3 AM — you find out when you press Build.

Built-in primitive types (reminder)

We saw most of these last chapter. A summary, plus some new ones:

CategoryTypesExample
Whole numbersbyte, short, int, long42
Unsigned whole numbersbyte, ushort, uint, ulong42u
Floating-pointfloat, double3.14
High-precision decimaldecimal19.99m
Truth valuesbooltrue
One characterchar'A'
Textstring"hello"
"Any object"object(anything)

Type conversions

Sometimes you really do need to turn one type into another. C# has two flavors of conversion.

Implicit conversions (safe, automatic)

If the conversion can't lose information, the compiler does it for you with no syntax:

Code Block
C# 13

Explicit conversions (you take responsibility)

If the conversion might lose information, the compiler refuses unless you cast explicitly with (type)value:

Code Block
C# 13

The (int) is the compiler saying "OK, you're on the hook for any data you lose."

Parsing strings

A very common operation: turn a string like "42" into an int.

Code Block
C# 13

int.Parse throws an exception on bad input. int.TryParse returns false and gives you a default value instead — that's usually the safer choice when input might be junk.

Value types vs reference types

This is the single most important distinction in the C# type system. Knowing it cleanly will save you from many head-scratching bugs.

The difference is in what gets copied when you assign or pass to a method.

Value types: assignment copies the value

Code Block
C# 13

a is still 5. Changing b did nothing to a. They are independent boxes that happened to hold the same number for a moment.

Reference types: assignment copies the reference

A reference type lives on the heap. The variable holds a reference (an address) to the object, not the object itself.

Code Block
C# 13

a and b are two names for the same list. Adding through b shows up through a, because there is only one list on the heap.

If you wanted a real copy, you'd ask explicitly:

Code Block
C# 13

Why strings act "like values"

Strings are reference types — but they are also immutable. You cannot change the contents of a string once it's created. Every "change" creates a new string.

Code Block
C# 13

Because strings can't be mutated, you get "value-like" behavior even though the underlying type is a reference type. This is why most programmers think of string as "basically a primitive."

Methods, references, and surprises

Reference behavior also matters when you pass objects to methods.

Code Block
C# 13

AppendOne reaches through the reference and changes the list. Reassign only changes its local copy of the reference, so the caller doesn't see it. This trips up beginners constantly. Take a minute to picture it:

nullable types

A reference variable can hold a special value called null, which means "no object at all."

Code Block
C# 13

In modern C#, string (without ?) means "definitely not null," and string? means "might be null." The compiler warns you if you forget to check.

We will come back to nulls in Error handling. For now, just know they exist, and that the ? is the language's way of saying "be careful."

Test your understanding

QuestionSelect one

Static typing means:

The values of variables can't change

The runtime checks types as the program runs

The compiler knows the type of every variable and refuses to compile code that uses values in a way that doesn't match their type

C# uses no types at all

QuestionSelect one

After this code runs:

var a = new List<int> { 1, 2 };
var b = a;
b.Add(3);

What is in a?

Hint: List<T> is a reference type — think about whether b = a copies the list or just another name for it.

[1, 2]

[3]

[1, 2, 3]

Compile error

QuestionSelect one

What's the safest way to convert a user-supplied string to an integer?

(int)userInput

int.Parse(userInput) and ignore the result

int.TryParse(userInput, out int n) and handle the false case

Convert it to double first and then to int

On this page