C# Data Types

Data types tell C# what kind of value a piece of data is: a whole number, decimal number, character, string of text, true-or-false value, date, object, and more. They matter because C# uses types to choose operations, reserve appropriate storage, catch mistakes before the program runs, and decide how values move through memory. Learning types well is one of the fastest ways to make your C# code clearer and less error-prone.

Overview: How C# Data Types Work

C# is a strongly typed, statically typed language. Strongly typed means values are not treated as interchangeable just because they look similar. The integer 42, the decimal 42.0m, the double 42.0, and the string "42" are different kinds of values. Statically typed means the compiler knows the type of each variable and expression before the program runs.

Most everyday C# types fit into two broad families: value types and reference types. Value types include int, double, decimal, bool, char, DateTime, enums, and structs. A variable of a value type directly contains its value. When you assign one value-type variable to another, the value is copied.

Reference types include string, arrays, classes, interfaces, delegates, and most objects you create with new. A reference-type variable contains a reference to an object managed by the CLR on the managed heap. Assigning a reference-type variable copies the reference, not the whole object. Two variables can therefore refer to the same object. Strings are reference types too, but strings are immutable, so operations that appear to change a string actually create a new string.

The Common Language Runtime, or CLR, represents C# values using .NET types. For example, the C# keyword int is an alias for System.Int32, string is an alias for System.String, and bool is an alias for System.Boolean. These aliases are idiomatic in C# source code, while the underlying .NET type names appear often in documentation and reflection.

Choosing the right type communicates intent. Use int for normal whole-number counts, long when the range can exceed about two billion, double for approximate measurements and scientific calculations, and decimal for money-like base-10 calculations. Use bool for yes-or-no state, char for one UTF-16 code unit, string for text, and nullable types when a value can genuinely be missing.

Syntax

int count = 12;
double temperature = 21.5;
decimal price = 19.99m;
bool isActive = true;
char grade = 'A';
string name = "Maya";
int? optionalScore = null;
Part Meaning
int count Declares a variable named count whose type is a 32-bit signed integer.
21.5 A double literal by default, useful for approximate fractional values.
19.99m A decimal literal. The m suffix is required for decimal constants.
'A' A char literal uses single quotes and stores one UTF-16 code unit.
"Maya" A string literal uses double quotes and stores text.
int? A nullable value type. It can contain either an int value or null.

Common Built-in Types

Type Use it for Example
int Most whole-number counts and indexes 42
long Very large whole numbers 5000000000L
double Approximate decimal measurements 98.6
decimal Money and precise base-10 quantities 10.25m
bool True-or-false conditions true
char A single character code unit 'x'
string Text "hello"

Examples

Using Basic Types Together

using System;

class Program
{
    static void Main()
    {
        string product = "Notebook";
        int quantity = 3;
        decimal unitPrice = 4.50m;
        bool inStock = true;
        char shelf = 'B';

        decimal total = quantity * unitPrice;

        Console.WriteLine($"Product: {product}");
        Console.WriteLine($"Shelf: {shelf}");
        Console.WriteLine($"Quantity: {quantity}");
        Console.WriteLine($"In stock: {inStock}");
        Console.WriteLine($"Total: {total}");
    }
}

Output:

Product: Notebook
Shelf: B
Quantity: 3
In stock: True
Total: 13.50

This program combines text, whole numbers, decimal arithmetic, Boolean state, and a character. The expression quantity * unitPrice produces a decimal because one operand is decimal. C# will not silently treat product as a number or inStock as text inside a calculation.

Value Type Copies and Reference Type References

using System;

class Program
{
    static void Main()
    {
        int originalCount = 5;
        int copiedCount = originalCount;
        copiedCount = 9;

        string firstName = "Lena";
        string sameName = firstName;
        sameName = sameName.ToUpperInvariant();

        Console.WriteLine($"originalCount: {originalCount}");
        Console.WriteLine($"copiedCount: {copiedCount}");
        Console.WriteLine($"firstName: {firstName}");
        Console.WriteLine($"sameName: {sameName}");
    }
}

Output:

originalCount: 5
copiedCount: 9
firstName: Lena
sameName: LENA

The two int variables become independent after assignment because the value is copied. The two string variables initially refer to the same string object, but ToUpperInvariant returns a new string. It does not edit the original string object, so firstName still prints Lena.

Choosing Numeric Types for a Calculation

using System;
using System.Globalization;

class Program
{
    static void Main()
    {
        int items = 7;
        double averageWeight = 1.25;
        decimal unitPrice = 2.99m;

        double totalWeight = items * averageWeight;
        decimal subtotal = items * unitPrice;

        Console.WriteLine($"Total weight: {totalWeight.ToString("F2", CultureInfo.InvariantCulture)} kg");
        Console.WriteLine($"Subtotal: {subtotal.ToString("F2", CultureInfo.InvariantCulture)}");
    }
}

Output:

Total weight: 8.75 kg
Subtotal: 20.93

This example uses double for a physical measurement and decimal for money. Both types can represent fractional values, but they are optimized for different jobs. A double is binary floating point and is fast for approximate measurement. A decimal stores base-10 decimal digits and is preferred when cents and rounding rules matter.

Nullable Value Types

using System;

class Program
{
    static void Main()
    {
        int? score = null;
        Console.WriteLine($"Has score: {score.HasValue}");

        score = 88;
        Console.WriteLine($"Has score: {score.HasValue}");
        Console.WriteLine($"Score: {score.Value}");
    }
}

Output:

Has score: False
Has score: True
Score: 88

A plain int always contains an integer value, but int? can also represent no value. Nullable value types are useful for optional database fields, forms, search filters, and calculations where absence is different from zero.

How Data Types Work Step by Step

  1. The compiler reads each declaration and records the variable name and compile-time type.
  2. Literal values are typed. For example, 12 is an int, 12.0 is a double, 12.0m is a decimal, and "12" is a string.
  3. For every expression, the compiler checks which operators are valid for the operand types. Numeric addition, string concatenation, and Boolean logic are different operations.
  4. If a conversion is needed, the compiler accepts it only when the conversion is allowed. Widening conversions such as int to long are often implicit. Narrowing conversions such as double to int require an explicit cast and may lose information.
  5. The compiler emits intermediate language that uses the selected .NET types. At runtime, the CLR executes those instructions and manages object memory for reference types.

Boxing is another important under-the-hood detail. When a value type such as int must be treated as object, the CLR boxes it by copying the value into an object on the managed heap. Unboxing extracts the value again. You do not need to avoid boxing everywhere as a beginner, but it explains why generic collections such as List<int> are preferred over older object-based collections.

Common Mistakes

Using the Wrong Literal Suffix

decimal price = 19.99;

This does not compile because 19.99 is a double literal, and C# does not implicitly convert double to decimal. Add the m suffix for decimal literals.

decimal price = 19.99m;
Console.WriteLine(price);

Output:

19.99

Expecting Integer Division to Keep the Fraction

using System;

class Program
{
    static void Main()
    {
        int correct = 2;
        int total = 3;

        double wrongRatio = correct / total;
        double rightRatio = (double)correct / total;

        Console.WriteLine(wrongRatio);
        Console.WriteLine(rightRatio);
    }
}

Output:

0
0.6666666666666666

The first division happens while both operands are still int, so the fractional part is discarded before the result is stored in a double. Cast one operand first when the division itself must be floating point.

Confusing char and string

char initial = "A";

This does not compile. Double quotes create a string, even if the text contains one character. A char literal uses single quotes.

char initial = 'A';
string name = "Ava";
Console.WriteLine(initial);
Console.WriteLine(name);

Output:

A
Ava

Best Practices

  • Use int for ordinary counts unless you have a real range reason to choose long, short, or byte.
  • Use decimal for money and human-entered decimal quantities where exact base-10 rounding matters.
  • Use double for approximate measurements, graphics, physics, statistics, and scientific-style calculations.
  • Do not store numbers in string variables just because they come from input. Parse them before calculating.
  • Prefer C# aliases such as int, string, and bool in normal source code.
  • Use nullable value types only when missing is a meaningful state, not as a substitute for choosing a good default.
  • Be explicit with casts that can lose data, and keep them close to the operation where the loss is intended.
  • Let the compiler help you. If a type error appears, fix the model of the data instead of forcing conversions blindly.

Practice Exercises

  1. Create variables for a person’s name, age, height in meters, and whether they have an active account. Print each value with a label.
  2. Write a receipt calculation using int for quantity, decimal for unit price, and decimal for total. Use at least one price with cents.
  3. Create an int? variable for an optional rating. Print whether it has a value, then assign a rating and print the value.

Summary

  • C# data types define what values can be stored and what operations are allowed.
  • Value types directly contain their data; reference types contain references to managed objects.
  • C# keywords such as int and string are aliases for .NET runtime types.
  • Numeric types should be chosen by meaning: counts, large values, approximate measurements, or decimal money.
  • Literal suffixes matter, especially m for decimal and L for long.
  • Nullable value types such as int? represent either a value or null.
  • The compiler’s type checking catches many bugs before the CLR runs your program.