C# · Beginner

Variables and Types in C#

10 min readUpdated September 24, 2026Every example verified

In short: A C# variable is a named storage location declared with a type, such as int count = 3;, and it can only hold values of that type. Whole numbers go in int or long, numbers with a fraction in double (fast, binary) or decimal (exact, for money), true or false in bool, one character in char and text in string. var asks the compiler to infer the type from the initial value; the variable is still.

Why every variable has a type

C# is statically typed: the type of every variable is fixed when it is declared and checked by the compiler. int count = 3; reserves space for a 32-bit whole number named count; assigning the text "three" to it later is a compile error, not a surprise at run time. That check is the main reason C# programs fail early and loudly rather than late and quietly, and it is what lets the editor offer the right completions after every dot.

The numeric types differ in what they can hold. int covers whole numbers from about minus 2.1 billion to plus 2.1 billion; long reaches about 9.2 quintillion either way. double stores numbers with a fractional part as 64-bit binary floating point, which is fast and fine for measurements but cannot represent 0.1 exactly, so 0.1 + 0.2 is not quite 0.3. decimal stores 28 to 29 significant digits in base ten, so sums of prices come out exact; its literals carry an m suffix (4.50m) and it is the type for money. float is a 32-bit floating-point type with an f suffix, used mostly in graphics code.

Beyond numbers, bool holds true or false and is the only type a condition accepts; char holds one character in single quotes, 'B'; string holds text in double quotes and is immutable, so every operation on a string produces a new one. Printing a bool gives True or False with a capital letter, because that is what its ToString() returns.

Arithmetic follows the operands. When both sides of / are integers the result is an integer and the remainder is discarded: 12 / 7 is 1, and % gives the remainder 5. Make one side a floating-point value, for example by casting, (double)slices / loaves, to get 1.714.... Conversions that cannot lose information happen automatically (int to long or double); conversions that can, such as double to int, need an explicit cast, (int)3.99, which truncates toward zero to 3. Use Math.Round first when you want the nearest whole number.

Input arrives as text, so it must be parsed. int.Parse(text), double.Parse(text) and decimal.Parse(text) convert, and throw a FormatException if the text is not a valid number. int.TryParse(text, out int value) returns false instead of throwing and leaves value at 0, which is the right tool when the input may be malformed. Going the other way, every value has a ToString(), and string interpolation calls it for you; a format specifier after a colon controls the shape, so {bill:F2} always shows two decimal places.

var lets you omit the type when the initial value makes it obvious: var total = count * 2; is an int, and the compiler treats it exactly as if you had written int. It must be initialised on the same line and cannot change type later; it is a typing shortcut, not dynamic typing. const marks a value that never changes, such as const int MaxSeats = 48;, and the compiler rejects any assignment to it. By convention local variables and parameters are camelCase, while types, methods and properties are PascalCase.

Syntax

 C# · syntax
type name = value;               // declare and initialise
int count = 3;                   // whole number
long population = 8_100_000_000; // larger whole number; _ separates digits (C# 7)
double ratio = 0.75;             // binary floating point
decimal price = 4.50m;           // exact decimal, m suffix
bool inStock = true;
char grade = 'B';
string label = "rye";
var total = count * 2;           // inferred as int; must be initialised
const int MaxSeats = 48;         // cannot be reassigned

int n = int.Parse(text);         // text -> int, throws on bad input
bool ok = int.TryParse(text, out int m); // false instead of throwing
double d = n;                    // implicit widening conversion
int back = (int)3.99;            // explicit cast: truncates to 3

A local variable must be assigned before it is read; the compiler reports an error for a variable that might still be unassigned.

Whole numbers, fractions and two kinds of division

Integer arithmetic, the double versus decimal difference, and how each type prints.

 C#
int loaves = 7;
int slices = 12;
Console.WriteLine(loaves * slices);
Console.WriteLine(slices / loaves);
Console.WriteLine(slices % loaves);
Console.WriteLine((double)slices / loaves);
double bag = 0.1 + 0.2;
decimal till = 0.1m + 0.2m;
Console.WriteLine(bag);
Console.WriteLine(till);
Console.WriteLine(bag == 0.3);
long population = 8_100_000_000;
char grade = 'B';
bool open = slices > loaves;
Console.WriteLine(population / 1_000_000);
Console.WriteLine(grade);
Console.WriteLine(open);

Output

84
1
5
1.7142857142857142
0.30000000000000004
0.3
False
8100
B
True

slices / loaves divides two ints, so the result is the whole-number part 1, and % supplies the remainder 5; casting one operand to double first gives the full quotient. 0.1 + 0.2 in double prints as 0.30000000000000004 because neither 0.1 nor 0.2 has an exact binary form, and the comparison with 0.3 is therefore False; the same sum in decimal is exactly 0.3. long holds the 8.1 billion that would overflow an int, and the bool prints as True.

Turning input text into numbers

Two lines of text become an int and a decimal, and the results are formatted in several ways.

 C#
string peopleText = Console.ReadLine()!;
string priceText = Console.ReadLine()!;
int people = int.Parse(peopleText);
decimal price = decimal.Parse(priceText);
decimal bill = people * price;
Console.WriteLine($"{people} people at {price} each = {bill}");
Console.WriteLine($"Formatted: {bill:F2}");
var share = bill / 2;
Console.WriteLine(share);
int rounded = (int)Math.Round(bill);
Console.WriteLine(rounded);
bool ok = int.TryParse("3h", out int parsed);
Console.WriteLine(ok);
Console.WriteLine(parsed);
Console.WriteLine(people + " " + price);

Input given to the program: 32.4

Output

3 people at 2.4 each = 7.2
Formatted: 7.20
3.6
7
False
0
3 2.4

int.Parse and decimal.Parse turn the text into values the program can multiply. {bill:F2} pads 7.2 to 7.20, which is what a receipt needs. var share is inferred as decimal because bill / 2 is a decimal. Math.Round on a decimal returns a decimal, so an explicit (int) cast is needed to store it in an int. TryParse reports False for 3h and leaves parsed at 0 instead of crashing. On the last line + joins an int, a string and a decimal into one string; each number is converted by its own ToString().

Common mistakes

  • Expecting 7 / 2 to be 3.5

    Why it goes wrong: Both operands are int, so the division is integer division and the result is 3. The fraction is discarded before anything is printed.

    Fix: Make at least one operand a double or decimal: 7 / 2.0, (double)a / b, or declare the variables as double.

     C# · fix
    int a = 7;
    int b = 2;
    Console.WriteLine((double)a / b); // 3.5
  • Storing money in a double

    Why it goes wrong: Binary floating point cannot represent most decimal fractions exactly, so a column of prices added in double can differ from the printed total by a cent, and 0.1 + 0.2 == 0.3 is false.

    Fix: Use decimal with the m suffix for currency and anything that must add up exactly.

     C# · fix
    decimal subtotal = 19.99m + 0.01m;
    Console.WriteLine(subtotal == 20m); // True
  • Assigning a double to an int without a cast

    Why it goes wrong: int whole = 3.7; fails with error CS0266: Cannot implicitly convert type 'double' to 'int', because the conversion could lose information and C# refuses to do that silently.

    Fix: Cast explicitly to truncate, or round first: (int)3.7 is 3 and (int)Math.Round(3.7) is 4.

  • Declaring var without a value

    Why it goes wrong: var x; gives error CS0818: Implicitly-typed variables must be initialized; the compiler needs the initial value to work out the type.

    Fix: Give the value on the same line, or write the type explicitly (int x;) and assign later.

Built-in types at a glance

TypeHoldsExample literalNotes
intwhole numbers, 32-bit42about plus or minus 2.1 billion; the default choice for counting
longwhole numbers, 64-bit8_100_000_000Labout plus or minus 9.2 quintillion; the L suffix marks a long literal
doublefractional, binary0.75fast; about 15 to 17 significant digits; not exact for 0.1
decimalfractional, base ten4.50mexact for money; 28 to 29 significant digits; slower
booltrue or falsetruethe only type an if condition accepts
charone character'B'single quotes
stringtext"rye"double quotes; immutable

Where you use this

A receipt program is a good place to see every type doing its job. Quantities are whole numbers, so they are int; unit prices are money, so they are decimal; the customer's name is a string; whether they hold a loyalty card is a bool. Reading each of those from input means parsing text into the right type, and the type chosen up front decides what the arithmetic later will do: an int quantity times a decimal price gives an exact decimal total, and {total:F2} prints it in the shape a receipt needs. When the input comes from a person rather than a fixed file, TryParse lets the program complain politely instead of crashing.

 C# · in practice
int quantity = int.Parse(Console.ReadLine()!);
decimal unitPrice = decimal.Parse(Console.ReadLine()!);
bool loyalty = Console.ReadLine() == "yes";
decimal total = quantity * unitPrice;
if (loyalty)
{
    total = total * 0.9m;
}
Console.WriteLine($"Total: {total:F2}");

Key points

  • Every variable has one fixed type, checked at compile time.
  • int and long for whole numbers; double for measurements; decimal (with the m suffix) for money.
  • Integer division discards the remainder and % returns it; cast one operand to get a fraction.
  • Widening conversions are automatic; narrowing ones need an explicit cast.
  • int.Parse converts text and throws on bad input; int.TryParse returns false instead.
  • var infers the type from the initial value and is still statically typed.
  • Format values inside interpolated strings with specifiers such as {x:F2}.

Try it yourself

The program reads a number of tickets and a price per ticket, but it stores the price as an int, so a price such as 12.50 cannot be parsed. Change the type of the price variable so that the program prints the total with two decimal places.

Your program
int tickets = int.Parse(Console.ReadLine()!);
int price = int.Parse(Console.ReadLine()!);
Console.WriteLine($"{tickets * price:F2}");
Input the program receives: 3 ↵ 12.50
Expected output: 37.50

Practise this

Open the C# playground

Frequently asked questions

What is the difference between double and decimal in C#?

double is 64-bit binary floating point: fast, with a huge range and about 15 to 17 significant digits, but unable to represent most decimal fractions exactly, which is why 0.1 + 0.2 prints 0.30000000000000004. decimal is a 128-bit base-ten type with 28 to 29 significant digits that stores 0.1 exactly, at the cost of speed and range. Use double for physical measurements and scientific work, and decimal for money and anything that must add up to the cent.

Is var in C# the same as dynamic typing?

No. var only asks the compiler to work out the type from the initialiser; after that the variable is exactly as fixed as if you had written the type. var n = 5; makes an int, and assigning text to n later is a compile error. The dynamic keyword is the one that defers type checks to run time, and it is rarely needed.

Why does 7 / 2 give 3 in C#?

Because both operands are integers, and / on two integers performs integer division, discarding the remainder. Convert one operand to a floating-point type first, 7 / 2.0 or (double)a / b, to get 3.5. The % operator gives the discarded remainder: 7 % 2 is 1.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with .NET SDK 8.0 at build time by the publishing checks, and the output shown is what it printed. Running C# inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with .NET SDK 8.0 locally.