Variables in C#: Declare, var, const vs readonly and Scope (.NET 10)

The first bug most beginners hit with variables in C# isn’t a crash. It’s a build that refuses to start, because a local variable might be read before it has a value:

decimal discount;
string guest = "Jenna Morales";
if (guest.StartsWith("J"))
    discount = 0.10m;

Console.WriteLine($"{guest} gets {discount * 100}% off");

// error CS0165: Use of unassigned local variable 'discount'

Quick answer: give it a value on every path, like decimal discount = 0m;. Below, I run that fix and the rest of the variable rules on .NET 10: var, const vs readonly, fields, scope and null warnings.

Why Does C# Say “Use of Unassigned Local Variable”?

C# checks definite assignment at compile time. If any path through the code could read a local before it’s assigned, the build stops with CS0165. Here the if can be false, so discount might never get a value.

Variables in C#: CS0165 use of unassigned local variable 'discount' build error in the VS Code terminal
The compiler sees a path where discount is never assigned and stops the build

Start the variable at 0m and the same program builds and prints Jenna Morales gets 10.00% off.

The same check runs on every else branch and switch case in C#, so a variable assigned in only some cases fails the same way. Fields are different; they get defaults automatically.

How to Declare and Initialize Variables in C#

A variable declaration is a type, a name and an optional starting value. You can declare several variables of one type in a single statement, or declare first and assign later, as long as you assign before you read.

My running sample is a hotel booking in Denver for Tyler Brooks: three nights at $189.

using System.Globalization;
CultureInfo.CurrentCulture = new CultureInfo("en-US");

string guest = "Tyler Brooks";     // type, name, value
int nights = 3;
decimal rate = 189.00m;
int adults = 2, kids = 1;          // two variables, one statement
string room;                       // declared now...
room = "Suite 412";                // ...assigned later

decimal total = nights * rate;
Console.WriteLine($"{guest}, {room}: {nights} nights x {rate:C}");
Console.WriteLine($"Total {total:C} for {adults + kids} guests");

Output:

Tyler Brooks, Suite 412: 3 nights x $189.00
Total $567.00 for 3 guests
C# declare and initialize variables: string, int and decimal variables for a Denver hotel booking with the total in the VS Code terminal
Declared with a value, declared in a group, and declared now but assigned later

The type decides what a variable can hold, so rate can’t take "189" as text. For the full list of built-in types and their ranges, see data types in C#. To move a value from one type to another, read type casting in C#.

Names are case-sensitive, so nights and Nights are two different variables. Locals use camelCase; the C# variable naming conventions cover the rest.

var: Implicitly Typed Local Variables

var tells the compiler to work out the type from the value on the right. The variable is still strongly typed; you just don’t write the type name. That’s also why var always needs a value on the same line.

var city = "Denver";
var nights = 3;
var total = nights * 189.00m;
Console.WriteLine(city.GetType().Name);
Console.WriteLine(nights.GetType().Name);
Console.WriteLine(total.GetType().Name);

Output:

String
Int32
Decimal

nights * 189.00m is an int times a decimal, so total became Decimal. I use var when the type is obvious from the right side, like new List<string>() from List in C#, and write the type when it isn’t.

Important: with nullable reference types on, var infers a nullable type for reference values, so var city = "Denver"; is a string? to the compiler.

Leave the value off and there’s nothing to infer from, so the build stops with CS0818:

var deposit;          // no value, so no type to infer
deposit = 250.00m;
Console.WriteLine(deposit);

Build output:

C:\DotNetDemo\Variables\VarNoValue.cs(1,5): error CS0818: Implicitly-typed variables must be initialized

The build failed. Fix the build errors and run again.

const vs readonly in C#

Both stop a value from changing, but at different times. A const is baked in at compile time and must be a literal-style value. A readonly field is set once at run time, in its declaration or in a constructor.

var booking = new Booking("Carmen Diaz", new DateOnly(2026, 11, 20));
Console.WriteLine($"Max stay: {Booking.MaxNights} nights");
Console.WriteLine($"{booking.Guest} arrives {booking.Arrival:yyyy-MM-dd}");

class Booking
{
    public const int MaxNights = 14;        // fixed at compile time
    public readonly DateOnly Arrival;       // fixed per object, at run time
    public readonly string Guest;

    public Booking(string guest, DateOnly arrival)
    {
        Guest = guest;                      // allowed: inside the constructor
        Arrival = arrival;
    }
}

Output:

Max stay: 14 nights
Carmen Diaz arrives 2026-11-20
constreadonly
When the value is setCompile timeRun time (declaration or constructor)
Allowed typesNumbers, bool, char, string, enumsAny type
Can differ per object?No, it’s implicitly staticYes
Local variable?Yes (const int x = 5;)No, fields only

Every booking shares MaxNights, while each one gets its own Arrival. Constants are PascalCase, as shown in the C# constant naming conventions.

Local, Instance and Static Variables

Where you declare a variable decides how long it lives and who shares it. Here two front desks check in three guests:

var lobby = new FrontDesk();
lobby.CheckIn("Jenna Morales");
lobby.CheckIn("Tyler Brooks");
var annex = new FrontDesk();
annex.CheckIn("Carmen Diaz");
Console.WriteLine($"Lobby {lobby.Count}, Annex {annex.Count}");
Console.WriteLine($"Hotel {FrontDesk.Total}");

class FrontDesk
{
    public int Count;              // instance field: one per desk
    public static int Total;       // static field: shared by every desk
    public void CheckIn(string guest)
    {
        string message = $"Welcome, {guest}";   // local: gone after the call
        Count++; Total++;
        Console.WriteLine(message);
    }
}

Output:

Welcome, Jenna Morales
Welcome, Tyler Brooks
Welcome, Carmen Diaz
Lobby 2, Annex 1
Hotel 3
C# local, instance and static variables: FrontDesk Count per object and static Total shared by all objects, VS Code output
Count is per desk (2 and 1); the static Total is shared by both desks (3)
KindDeclaredLivesDefault if not set
LocalInside a method or blockUntil the block endsNone, must assign (CS0165)
Instance fieldIn a class, no staticAs long as its objectType’s default
Static fieldIn a class with staticWhole app runType’s default
ParameterIn the method signatureFor that callThe caller’s argument

Parameters are variables too; functions in C# covers how they’re passed, including ref and out.

Variable Scope and Blocks

A local variable exists only inside the braces where you declare it. Each pass of a loop gets a fresh one, and code after the closing brace can’t see it.

int rooms = 3;
for (int i = 1; i <= rooms; i++)
{
    int floor = i + 1;               // a fresh floor on every pass
    Console.WriteLine($"Room {i} is on floor {floor}");
}
// Console.WriteLine(floor);  // CS0103: floor doesn't exist here

Output:

Room 1 is on floor 2
Room 2 is on floor 3
Room 3 is on floor 4

Uncomment the last line and the build fails with CS0103, because floor ended with the loop. The loop counter i follows the same rule, as the for loop in C# shows.

What Default Value Does a C# Variable Get?

Fields and properties you never assign start at their type’s default: zero for numbers, false for bool, null for reference types and the zero value for structs.

using System.Globalization;
CultureInfo.CurrentCulture = new CultureInfo("en-US");

var g = new Guest();
Console.WriteLine($"int: {g.Nights}, decimal: {g.Balance}, bool: {g.IsVip}");
Console.WriteLine($"string is null: {g.Name is null}");
Console.WriteLine($"DateOnly: {g.Arrival}");
Console.WriteLine($"default(double): {default(double)}");

class Guest
{
    public int Nights { get; set; }
    public decimal Balance { get; set; }
    public bool IsVip { get; set; }
    public string? Name { get; set; }
    public DateOnly Arrival { get; set; }
}

Output:

int: 0, decimal: 0, bool: False
string is null: True
DateOnly: 1/1/0001
default(double): 0

The empty DateOnly printed 1/1/0001, the year-1 minimum. The default keyword gives you the same value anywhere, for example default(double). Struct fields work the same way; see structures in C#.

Nullable Reference Type Variables and CS8600 Warnings

New projects and file-based apps turn on nullable reference types. A plain string then promises it’s never null, and string? says null is allowed. Break that promise and you get a warning, not an error.

string? middle = MiddleName("Tyler Brooks");   // ? = null is allowed
string shown = middle;                        // warning CS8600
Console.WriteLine($"Middle name: {shown ?? "(none)"}");
Console.WriteLine($"Length: {middle?.Length ?? 0}");

static string? MiddleName(string fullName)
{
    string[] parts = fullName.Split(' ');
    return parts.Length == 3 ? parts[1] : null;
}

Build and run output:

C:\DotNetDemo\Variables\NullableWarnings.cs(2,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
Middle name: (none)
Length: 0
C# nullable reference types: string? variable and CS8600 converting null literal or possible null value warning in the VS Code terminal
Assigning a string? to a plain string gives warning CS8600, but the program still runs

The ?. and ?? operators read a maybe-null variable safely. For other ways to test for null, see how to check for null values in C#.

Printing Variables with String Interpolation (and Swapping Two)

Put a $ before a string and any variable inside braces is printed in place. A colon adds a format, and a comma pads to a width. A tuple swaps two variables without a temp variable.

using System.Globalization;
CultureInfo.CurrentCulture = new CultureInfo("en-US");

string first = "Jenna", last = "Morales";
decimal balance = 1249.5m;
Console.WriteLine($"Guest: {last}, {first}");
Console.WriteLine($"Balance: {balance:C} |{balance,10:N2}|");

int checkIn = 18, checkOut = 15;              // typed backwards
(checkIn, checkOut) = (checkOut, checkIn);    // swap with a tuple
Console.WriteLine($"Stay: Nov {checkIn} to Nov {checkOut}");

Output:

Guest: Morales, Jenna
Balance: $1,249.50 |  1,249.50|
Stay: Nov 15 to Nov 18

The {balance,10:N2} part right-aligned 1,249.50 in ten characters, and the tuple fixed the backwards dates. The classic temp-variable and arithmetic ways are in swap two numbers in C#.

Common Variable Errors in C# and How to Fix Them

I broke the hotel code on purpose to collect the errors I see most in beginner code reviews:

const int MaxNights = 14;
MaxNights = 21;                    // change a constant

var room = new Room();
room.Number = 412;                 // set readonly outside the constructor

for (int i = 0; i < 2; i++) { int floor = i; }
Console.WriteLine(floor);          // use a variable outside its block

class Room { public readonly int Number = 101; }

Build output:

C:\DotNetDemo\Variables\Errors.cs(2,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
C:\DotNetDemo\Variables\Errors.cs(5,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer)
C:\DotNetDemo\Variables\Errors.cs(8,19): error CS0103: The name 'floor' does not exist in the current context

The build failed. Fix the build errors and run again.
C# variable compiler errors CS0131 constant assignment, CS0191 readonly field and CS0103 name does not exist in the VS Code terminal
Three common variable mistakes, each with its own compiler error
ErrorCauseFix
CS0165Local read before it’s assignedAssign on every path, or at declaration
CS0131Assigning to a constUse a normal variable or readonly field
CS0818var with no valueInitialize it, or write the type
CS0191Setting a readonly field outside the constructorSet it in the constructor or an init property
CS0103Using a variable outside its blockDeclare it in the outer scope

Errors like these stop the build. Values that go wrong while the app runs need try catch in C# instead.

Which Kind of Variable Should You Use?

  • A local for anything one method needs, declared close to where you use it.
  • var when the type is plain from the right side; the type name when it isn’t.
  • const for values that never change, like a tax rate in the law; readonly for values fixed per object.
  • An instance field for an object’s own data, a static field only for data every object shares.
  • string? (and int?) when “no value” is a real answer.

These go further on the topics around variables:

Frequently Asked Questions

What is a variable in C#?

A variable is a named storage location with a fixed type. You declare it with a type and a name, like int nights = 3;, and the compiler only lets it hold values of that type.

Does var make C# dynamically typed?

No. var only asks the compiler to infer the type from the initializer. The variable keeps that type for its whole life, so var nights = 3; is an int and can’t later hold a string.

What is the difference between const and readonly in C#?

A const is a compile-time value that’s the same everywhere and is implicitly static. A readonly field is set once at run time, in its declaration or a constructor, so each object can have its own value.

Do C# local variables have default values?

No. A local must be assigned before it’s read, or the build fails with CS0165. Fields, array elements and static variables do get default values, such as 0, false or null.

How do I fix “Use of unassigned local variable” in C#?

Give the variable a value when you declare it, or make sure every if/else path and every switch case assigns it before the first read. Microsoft’s rules are in Declaration statements on Microsoft Learn.