C# Data Types: Different Types of Data Explained

csharpdatatypesC# is a modern, high-powered, developer-friendly programming language. As the chief language in the .NET framework, C# is used in most important Windows applications. Since the language is based on the C language, it also finds a lot of applications in research and enterprise areas as well.

In this blog post, we will cover one of the core foundations of C# – C# data types.  A C# data type will fall into one of the following three categories: value types, reference types, and pointer types.  We will give a short overview of each category’s important concepts, and we will also list the built-in C# types that are currently in each category. If you’re getting started with C#, check out this course on learning C# in one hour.

Data Types in C#

As mentioned above, there are three data types in C#:

1. Value Types

Conceptually, a value type represents a very simple blob of data (e.g. a number like 123 or a character like k).  In C#, value types have three major traits:

  • Copy-by-value semantics

  • Value equality semantics

  • Stack memory allocation

When an instance of a value type is passed around in a program, it is copied each time.  When a copy is edited, the original version does not change.

When two instances of a value type are checked for equality, the two instances are considered equal if their underlying values are the same.

In C#, value types are allocated on the stack rather than the heap.  Depending on the size of the type and its usage, this could affect performance, although in most cases it’s not likely to matter.

It’s also worth noting that C# supports user-defined value types.  These are called structs.  Structs allow you to define custom fields and methods on a type while still preserving the three traits listed above.

The following is a list of value types currently built into C#:

Type

Range

Size

bool

true/false

1 byte

byte

-128 to 127

1 byte

sbyte

0 to 255

1 byte

char

unicode; 0x0000 to 0xFFFF

2 bytes

ushort

0 to 65535

2 bytes

short

-32768 to 32767

2 bytes

uint

0

to

4,294,967,295

4 bytes

int

-2,147,483,648

to

2,147,483,647

4 bytes

ulong

0

to

18,446,744,073,709,551,615

8 bytes

long

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

8 bytes

float

-3.4 × 1038

to

3.4 × 1038

4 bytes

double

±5.0 × 10−324

to

±1.7 × 10308

8 bytes

decimal

(-7.9 x 1028 to 7.9 x 1028) / (100 to 28)

16 bytes

struct

varies / user-defined

varies / user-defined

Starting out with C#? Check out this course on basic C# with Visual Basic.

2. Reference Types

Conceptually, a reference type represents a potentially large piece of data that is passed around a program via a reference to that data.  Reference types tend to be more complex in nature than value types.

Reference types have three major traits of their own:

  • Copy-by-reference semantics

  • Identity equality semantics

  • Heap memory allocation.

When an instance of a reference type is passed around in a program, a reference (i.e. a small, unique identifier) to the instance’s data is created.  The data itself is not copied.  Finally, when the copied instance is edited, what’s actually being edited is the original data.

When two instances of a reference type are checked for equality, the two instances are considered equal only if they both point to the same piece of data (i.e. point to the same memory address).

In C#, reference types are allocated on the heap rather than the stack (i.e. opposite of value types).  Depending on the usage of the type, this could affect performance, although in most cases it’s not likely to matter.

It’s also worth noting that C# supports user-defined reference types.  These are called classes.  Classes allow you to define custom fields and methods on a reference type.

Finally, strings are a special and potentially confusing reference type.  Strings are sequences of characters (e.g. apple or orange). Even though they are reference types, they were implemented to behave like value types with respect to their copy semantics and their identity semantics.

A string is actually immutable, and editing a copied string will not change the original string.

A string is considered to be equal to another string if the two strings point to an equivalent sequence of characters, regardless of where in physical memory those character sequences reside.

The following is a list of reference types currently built into C#:

Type

Usage

object

Base type that can be used to reference any reference type.  Can also hold (“box”) any value type.

string

Holds a sequence of characters.  Special reference type that behaves like a value type.

class

User-defined reference type.

interface

User-defined contract.  Classes (and also structs) can implement interfaces.  This is done to conform to various APIs which only operate on particular interfaces.

delegate

Abstraction of a method.  The method can be a static method or an instance method.

dynamic

Provides functionality that is typically found in dynamic languages.  Methods and properties can be added and removed from dynamic instances at run-time.

Want to make games in Unity? Learn more about scripting for games using C# in this course.

3. Pointer Types

Besides value types and reference types, C# supports one other category of data: pointer types.

Using pointer types inside unsafe contexts is the only way to access memory directly in C#.  Pointers in C# are similar to C/C++ pointers.

Deliberate or accidental misuse of unsafe code can create security vulnerabilities as well as unstable programs.  Because of this, avoiding unsafe code is generally considered to be a best practice.

Data types are one of the core foundations of C#.  In this article, we have taken a look at the three categories a C# data type can fall into: value types, reference types, and pointer types.  For a more in-depth exploration of data types and other C# concepts, check out this course on exploring the fundamentals of C# programming.