To kick off our C# in Simple Terms mega-series, let's learn about the most fundamental concept C# has: the type system.
The Sample Solution
As always, there's a sample C# project over on GitHub that demos the ideas we are talking about in this project. The solution contains all parts of this series.
Strongly-Typed and Type-Safe
The first and most important thing to know about C# as a programming language is this: C# is a strongly-typed language. This means that every variable, every constant, every class, every single object ever created using C# has a type. It is impossible for an object to exist in C# without it having a type.
A type in C# (and .NET) is a set of properties about a specific kind of object. These might include the storage space it needs, the maximum or minimum size of the object, and others.
int four = 4; //Max value 2147483647 (2^31 - 1), Min value 2147483648 (-2^32) double twopointfive = 2.5; //Size: 8 bytes char a = 'a'; //Size: 16 bit
The lines above demonstrate how to create a variable in C#. The first word in each line above is the type (e.g.
char), the second word is the variable name (e.g.
a) and the number or character on the right side of the
= is the variable's value.
Because C# is a strongly-typed language, C# is also a type-safe language. That means that objects which are instantiated as a given type (number, character, date, class, etc.) have rules in place to ensure that said instance behaves as that type. Consequently the C# compiler will allow us to add two number types together, but trying to add a number to a word will cause a error.
Because no object can exist in C# without a type, there exists a "base" type that every kind of object inherits from.
In .NET, that "base" type is called
System.Object newObject = new System.Object();
We can also instantiate this object using a simplified syntax:
object newObject = new object();
C# and .NET support the concept of type inheritance. Types can "derive" (meaning they inherit attributes, properties, and constraints) from other types. For example, all types in C# derive from the base class
We will discuss inheritance, particularly inheritance involving classes, more thoroughly in Part 9 of this series, which covers Inheritance and Polymorphism; for now, just know that types can inherit behavior from other types.
Value Types and Reference Types
C# supports two distinct kinds of types: value types and reference types.
Value types are objects whose value is contained by the object. In C#, value types include almost all "primitive types", which we will discuss in the next part of this series. Generally these are "simpler" types, like numbers or characters.
In C#, all value types inherit from a base class
System.ValueType, which in turn inherits from
System.Object, as all types must.
int myNumber; //Default value 0 bool myBool; //Default value false double myDouble; //Default value 0
If we do not specify a value when creating a value type, they get a default value.
Reference types, unlike value types, are objects which exist on the memory heap. The variables we create contain a "pointer" or a "reference" to that value on the memory heap; the variable does not contain the value itself. Generally, reference types are more complex types, such as custom classes, queries, and collections (like arrays).
A reference type, if it is initialized without a value, will have the value null. We will discuss null later in this post.
Because reference types are merely a reference (or pointer) to the object's value, it is possible for objects on the heap to no longer have any references to them. C# includes a feature called garbage collection, where automatic memory management will "reclaim" memory from reference types and other sources that are no longer being used. In most cases, you don't need to worry about the garbage collection process; it just happens behind the scenes and doesn't interfere with anything.
Implicit Types (var)
If you've read any C# code, you have probably encountered something like this:
var myValue = 5;
var is a special keyword in C#. It is a "placeholder" for a type which will be determined by the value of the variable. For example, in the line above "myValue" is initialized as type
int, because the value assigned to it is a simple integer (5).
It is important to note that
var is not a type unto itself. It is only a placeholder.
var, we can make our code easier to read. For example, imagine we have a set of variable declarations:
double myDouble = 5.6; char myChar = 'a'; MyClass myClass = new MyClass(); int myInt = 7;
var keyword, we can simplify our code a bit:
var myDouble = 5.6; var myChar = 'a'; var myClass = new MyClass(); var myInt = 7;
In this way, each of these variables has their type implicitly assigned from their value.
We must also talk about a special type in C#:
Null is a literal type that represents a null reference; that is, one which does not point to an object on the memory heap. It is also the default value of reference types when they are created.
//The below two lines will be treated as identical. MyClass myClass = null; MyClass myClass;
null when a reference type object does not yet have a value. Instances of reference types, by default, have the value
When writing code, we often have to do null checking, which is when we confirm that a particular object is or is not currently
However, we cannot use the
var keyword and assign the variable to
null; that will throw a compilation error. This is because the C# compiler cannot determine the type of a variable if the value is
- Strongly-typed: Every object must have a defined type.
- Type: a set of properties about a given object.
- Type-safe: Objects which are created as a type will behave as that type. Alternately: types cannot be combined in an unsafe manner.
- Type Inheritance: Types can assume the properties, attributes, and constraints of another type.
- Instance - A object created from a definition, where the definition can be a class, struct, value, etc. Instances are said to be instantiated from classes, structs, or other C# objects.
- Instantiate - Create an instance of. For example, in the line
int myYear = 2020;,
myYearis an instance of type
- Value type: An object which carries its value around with it.
- Reference type: An object which is a pointer or reference to a location in the memory heap, where the value of the object exists.
- Garbage collection: A feature of C# where unused allocations in the memory heap are automatically freed.
- Null: A literal type in C# that represents an unknown value.
- Null-checking: A programming process or pattern where reference types are checked for being null prior to being used.
object- A "shortcut" reference to the root type
var- A placeholder for a type. The actual type is determined by the value of the instance.
null- A special value that represents a missing reference.
The type system in C# is robust, able to support value and reference types as well as the special type null, though sometimes we need to use null-checking before we manipulate objects that can be null.
All types support type inheritance, where types may adopt the properties and constraints of other types, and because C# is a strongly-typed language, every object ever created must have a type. We can create implicit variables using the
var keyword, and every single object in C# will inherit from the base class
Thanks for reading! In the next post, we'll discuss the basic types available to us in C#, as well as literal and nullable types. Stick around!
Oh, and if you've got questions about C# type's system, I'm here to help! Ask questions in the comments below!
Did you enjoy this post? Then you'll love my premium newsletter The Catch Block! Issues come out every Wednesday, and they have the best links to quality stories from all around the ASP.NET and web programming worlds. Plus, original stories, tips, tutorials and sample code you won't find anywhere else. Even better, it's only $5/month, or $55/year! Check out the previous issues, and then sign up to get The Catch Block today!