Back to all articles

DotNet

C# Data Types Explained in Depth: Value Types vs Reference Types

A complete guide to C# data types, including value types, reference types, integers, float, double, decimal, string, arrays, classes, interfaces, delegates, var, dynamic, boxing, and nullable types.

12 min read

0 comments

Understanding data types is one of the most important foundations in C#.

Whenever you create a variable, C# needs to know what kind of data that variable will store.

For example:

int age = 25;
string name = "Shreya";
bool isActive = true;
decimal salary = 50000.50m;

Here:

  • int stores a whole number
  • string stores text
  • bool stores true or false
  • decimal stores a high-precision decimal number

But C# data types are not just about what kind of value they store.

They are also important because different types behave differently in memory.

At a high level, C# types are commonly divided into:

C# Data Types

Value Types
├── Integer
│   ├── byte
│   ├── short
│   ├── int
│   └── long
│
├── Decimal numbers
│   ├── float
│   ├── double
│   └── decimal
│
├── bool
├── char
├── enum
└── struct

Reference Types
├── string
├── object
├── arrays
├── class
├── interface
└── delegate

In this article, we will understand each of these in depth.

---

1. What is a Data Type in C#?

A data type tells C#:

  • what kind of value a variable can store
  • how much memory may be required
  • what operations are allowed
  • how the value behaves when assigned or passed around
  • what range or precision the value supports

Consider:

int age = 25;

C# knows that age contains an integer.

Because it is an int, we can perform arithmetic:

age = age + 1;

But this would not be valid:

age = "Twenty Five";

because "Twenty Five" is a string, not an int.

This type checking is one reason C# is called a statically typed language.

The compiler knows the type of most variables before the program runs.

---

2. Value Types vs Reference Types

This is the most important distinction to understand.

Value types

A value-type variable contains its value directly.

For example:

int a = 10;
int b = a;

At this point:

a = 10
b = 10

Now change b:

b = 20;

Result:

a = 10
b = 20

Changing b does not change a.

Why?

Because the value was copied.

Conceptually:

a  ---> 10

b  ---> 10

Each variable has its own value.

Common value types include:

int
long
float
double
decimal
bool
char
enum
struct

---

Reference types

A reference-type variable generally holds a reference to an object rather than containing that entire object value directly.

For example:

int[] first = { 10, 20, 30 };

int[] second = first;

Now both variables refer to the same array.

Conceptually:

first  ----\
            ---> [10, 20, 30]
second ----/

If we write:

second[0] = 100;

then:

Console.WriteLine(first[0]);

prints:

100

because both variables refer to the same array.

This difference between copying a value and copying a reference is fundamental in C#.

---

3. Integer Types

Integer types store whole numbers.

Examples:

0
10
-15
5000
1000000

They do not store fractional values such as:

10.5
3.14

C# provides several integer types.

---

byte

A byte is an 8-bit unsigned integer.

Range:

0 to 255

Example:

byte marks = 95;

This is valid:

byte value = 200;

This is not:

byte value = 300;

because 300 exceeds the maximum byte value.

When might byte be useful?

It is commonly useful when working with:

  • raw binary data
  • files
  • network packets
  • images
  • streams

For example:

byte[] imageData;

---

sbyte

There is also a signed version of byte:

sbyte temperature = -20;

Range:

-128 to 127

The s means signed.

Signed types can represent negative values.

Unsigned types cannot.

---

4. short

A short is a 16-bit signed integer.

Example:

short year = 2026;

Range:

-32,768 to 32,767

There is also:

ushort

which is unsigned.

Its range is:

0 to 65,535

In normal application development, short is used less frequently than int.

---

5. int

int is the most commonly used whole-number type in C#.

Example:

int age = 25;
int employeeCount = 500;
int marks = 95;

int is a 32-bit signed integer.

Its approximate range is:

-2.1 billion to +2.1 billion

More exactly:

-2,147,483,648
to
2,147,483,647

For most everyday whole numbers, int should be your default choice.

---

uint

uint means:

unsigned integer

Example:

uint population = 100000;

It does not allow negative values.

Its range is roughly:

0 to 4.29 billion

Even though uint provides a larger positive range than int, int is usually more convenient because many .NET APIs use signed integers.

---

6. long

When int is not large enough, use long.

Example:

long worldPopulation = 8_000_000_000L;

The suffix L tells C# that the literal should be treated as a long.

long is a 64-bit signed integer.

Its range is extremely large:

approximately
-9.22 quintillion
to
+9.22 quintillion

Examples where long can be appropriate include:

  • large database identifiers
  • timestamps
  • file sizes
  • very large counters
  • large numeric calculations

There is also an unsigned version:

ulong

---

7. Floating-Point and Decimal Types

C# provides three important types for numbers containing fractional parts:

float
double
decimal

They are not interchangeable in every situation.

---

8. float

A float is a 32-bit floating-point number.

Example:

float temperature = 36.5f;

Notice the f.

Without it:

float temperature = 36.5;

C# treats 36.5 as a double by default, so this causes a conversion issue.

Correct:

float temperature = 36.5f;

Approximate precision:

6–9 significant decimal digits

Example:

float value = 1.23456789f;

A float may not preserve every digit exactly.

Common uses

float can be useful for:

  • graphics
  • game coordinates
  • physics calculations where extreme precision is unnecessary
  • large collections where memory size matters

---

9. double

double is a 64-bit floating-point type.

Example:

double pi = 3.141592653589793;

Approximate precision:

15–17 significant digits

It is much more precise than float.

Also, decimal-looking numeric literals are double by default:

var number = 10.5;

Here, number becomes a double.

Common uses

double is appropriate for many:

  • scientific calculations
  • mathematical calculations
  • measurements
  • statistics
  • geometry

---

10. decimal

decimal is designed for high-precision decimal arithmetic.

Example:

decimal salary = 50000.75m;

Notice the m suffix.

decimal price = 99.99m;

Approximate precision:

28–29 significant digits

decimal is commonly used for:

  • money
  • salaries
  • prices
  • taxes
  • accounting
  • financial calculations

Why not always use double?

Because binary floating-point values such as double may not represent some simple decimal fractions exactly.

For example:

double a = 0.1;
double b = 0.2;

Console.WriteLine(a + b);

The internal result may contain a tiny representation error.

For finance, this can be undesirable.

Using:

decimal a = 0.1m;
decimal b = 0.2m;

is usually more appropriate.

---

11. float vs double vs decimal

A useful comparison:

TypeSizeApprox. precisionTypical use
float4 bytes6–9 digitsGraphics, games
double8 bytes15–17 digitsGeneral scientific calculations
decimal16 bytes28–29 digitsFinance and money

A simple rule:

Graphics / lower precision → float
General calculation        → double
Money                       → decimal

---

12. bool

bool represents Boolean logic.

It has two values:

true
false

Example:

bool isActive = true;
bool isDeleted = false;

It is commonly used in conditions:

if (isActive)
{
    Console.WriteLine("Employee is active");
}

Boolean variables are extremely common in business applications.

Examples:

bool isAdmin;
bool isApproved;
bool hasPermission;
bool isLoggedIn;

Good Boolean variable names often start with:

is
has
can
should

For example:

bool hasAccess = true;
bool canEdit = false;

---

13. char

A char stores one UTF-16 code unit.

Simple examples:

char grade = 'A';
char symbol = '#';
char digit = '5';

A char uses single quotes:

'A'

A string uses double quotes:

"A"

These are different:

char letter = 'A';
string text = "A";

Even though both visually contain A, their types are different.

---

14. enum

An enum represents a fixed set of named constants.

Imagine an employee can have one of these statuses:

Active
Inactive
Suspended

Instead of storing arbitrary strings:

string status = "Active";

we can create:

enum EmployeeStatus
{
    Active,
    Inactive,
    Suspended
}

Then:

EmployeeStatus status = EmployeeStatus.Active;

This is safer because the compiler restricts us to valid values.

For example:

if (status == EmployeeStatus.Active)
{
    Console.WriteLine("Employee can work");
}

Enums improve:

  • readability
  • type safety
  • maintainability

---

15. struct

A struct is a custom value type.

For example:

struct Point
{
    public int X;
    public int Y;
}

Then:

Point p1 = new Point();

p1.X = 10;
p1.Y = 20;

Because a struct is a value type, assignment copies its data.

Point p2 = p1;

p2.X = 100;

Now:

p1.X = 10
p2.X = 100

because p2 received a copy.

Structs are commonly suitable for small values that conceptually represent a single piece of data.

Examples in .NET include types such as:

DateTime
TimeSpan
Guid
decimal

These are structs.

---

16. Reference Types

Now let's move to reference types.

Common reference types include:

string
object
array
class
interface
delegate

---

17. string

A string represents text.

Example:

string name = "Shreya";
string city = "Delhi";

Strings are reference types.

But strings behave differently from many other reference types because they are immutable.

Immutable means:

Once a string object is created, its contents cannot be changed.

Consider:

string name = "Shreya";

name = name + " Kumari";

It may look like the original string was modified.

Conceptually, however, a new string is created and the variable refers to the new string.

This is important when performing many string modifications.

For heavy string building, C# provides:

StringBuilder

---

18. object

object is the ultimate base type of C#'s unified type system.

You can write:

object a = 10;
object b = "Hello";
object c = true;

Because every C# type can be represented as object.

However, storing value types inside object can involve boxing.

Example:

int number = 10;

object obj = number;

The integer value is boxed.

Getting it back:

int result = (int)obj;

is called unboxing.

Modern C# code generally prefers generics and strongly typed variables instead of relying heavily on object.

---

19. Arrays

An array stores multiple values of the same type.

Example:

int[] numbers = { 10, 20, 30 };

String array:

string[] names =
{
    "A

Comments

Join the discussion. Please keep comments respectful and relevant.

Loading comments...