Data Types
Every value in C# has a type that decides what it can hold, how much memory it uses and what you can do with it. This lesson tours the built-in types — integers, floating-point and decimal numbers, booleans, characters and strings — and introduces the important split between value types and reference types.
The built-in types
| Keyword | .NET type | Holds | Size |
|---|---|---|---|
int | Int32 | Whole numbers, about ±2.1 billion | 4 bytes |
long | Int64 | Whole numbers, about ±9.2 quintillion | 8 bytes |
short / byte | Int16 / Byte | Small whole numbers (byte: 0–255) | 2 / 1 bytes |
double | Double | Floating-point numbers, ~15–17 significant digits | 8 bytes |
float | Single | Floating-point, ~6–9 digits | 4 bytes |
decimal | Decimal | Exact base-10 numbers with 28–29 digits — for money | 16 bytes |
bool | Boolean | true or false | 1 byte |
char | Char | One UTF-16 character, such as 'A' | 2 bytes |
string | String | Text of any length | varies |
There are also unsigned versions (uint, ulong, ushort) and sbyte, but you'll rarely need them. For everyday code: int for counts, double for measurements, decimal for money, bool, char and string.
Ranges and sizes
Each numeric type exposes its limits as MinValue and MaxValue, and sizeof gives its size in bytes:
Console.WriteLine($"int: {int.MinValue} to {int.MaxValue}"); Console.WriteLine($"long: {long.MinValue} to {long.MaxValue}"); Console.WriteLine($"byte: {byte.MinValue} to {byte.MaxValue}"); Console.WriteLine($"sizes: int {sizeof(int)}, long {sizeof(long)}, double {sizeof(double)}, decimal {sizeof(decimal)}, char {sizeof(char)}");
int: -2147483648 to 2147483647 long: -9223372036854775808 to 9223372036854775807 byte: 0 to 255 sizes: int 4, long 8, double 8, decimal 16, char 2
Number literals
A whole number like 42 is an int, and a number with a decimal point like 4.2 is a double. A suffix picks another type. Underscores make long numbers readable, and 0x / 0b write hexadecimal and binary:
long distance = 9_460_730_472_580_800L; // L = long float ratio = 0.5f; // f = float decimal price = 19.99m; // m = decimal int mask = 0b1010_1010; // binary int colour = 0xFF8800; // hexadecimal double avogadro = 6.022e23; // scientific notation Console.WriteLine(distance); Console.WriteLine(ratio); Console.WriteLine(price); Console.WriteLine(mask); Console.WriteLine(colour); Console.WriteLine(avogadro);
9460730472580800 0.5 19.99 170 16746496 6.022E+23
Forgetting the m on a decimal is a common mistake — the compiler won't silently turn a double into a decimal:
decimal price = 19.99;
Program.cs(1,17): error CS0664: Literal of type double cannot be implicitly converted to type 'decimal'; use an 'M' suffix to create a literal of this type
Booleans
A bool holds true or false, usually the result of a comparison. Unlike C, numbers are not booleans: you can't write if (1).
int age = 20; bool isAdult = age >= 18; bool hasTicket = false; Console.WriteLine(isAdult); Console.WriteLine(isAdult && hasTicket);
True False
Characters
A char is one character in single quotes. Under the hood it's a 16-bit number (a UTF-16 code unit), so you can convert it to and from int:
char grade = 'B'; char next = (char)(grade + 1); Console.WriteLine(grade); Console.WriteLine((int)grade); Console.WriteLine(next); Console.WriteLine(char.IsLetter(grade)); Console.WriteLine(char.IsDigit('7'));
B 66 C True True
Default values
Every type has a default value: zero for numbers, false for bool, the zero character for char, and null (no object) for reference types like string. Fields of a class start out with these defaults, and you can ask for one with default:
Console.WriteLine(default(int)); Console.WriteLine(default(bool)); Console.WriteLine(default(decimal)); Console.WriteLine(default(char) == '\0');
0 False 0 True
Value types and reference types
C# types come in two families, and the difference affects how assignment works:
- Value types — all the numeric types,
bool,char, and structs (lesson 40). The variable holds the value itself. Assigning copies the value. - Reference types —
string, arrays, classes and lists. The variable holds a reference to an object stored elsewhere (on the heap). Assigning copies the reference, so both variables point at the same object.
int a = 10; int b = a; // copies the value b = 99; Console.WriteLine($"a = {a}, b = {b}"); int[] first = [1, 2, 3]; int[] second = first; // copies the reference second[0] = 99; Console.WriteLine($"first[0] = {first[0]}");
a = 10, b = 99 first[0] = 99
Changing b didn't affect a, but changing the array through second also changed first — they're the same array. Strings are reference types too, but they can never be modified (lesson 16), so they behave like values in practice.
Everything is an object
All types derive from object, so every value has methods such as ToString() and GetType():
Console.WriteLine(42.ToString() + "!"); Console.WriteLine(3.5.GetType()); Console.WriteLine("hi".GetType()); Console.WriteLine(typeof(bool));
42! System.Double System.String System.Boolean
Try it yourself
Print the largest long, then add 1 to int.MaxValue stored in a long variable. Lesson 8 shows what happens if you do it with an int.
9223372036854775807 2147483648 short holds -32768 to 32767