Back to Blog
C#

C# Variable Declaration: Syntax and Practical Usage

Learn how to declare variables in C# with explicit types, var, and nullable annotations, and understand how defaults, scope, and readability affect your choices.

variable declarationvar keywordnullable typesC# syntaxtype inference
Illustration of C# variable declaration showing type, name, and value assignment in a code editor.

C# variable declaration follows a simple pattern: a type, a name, and an optional initial value. The simplest form is:

int count;

This declares an integer variable named count without assigning a value. A declared-but-uninitialized field is automatically set to the type's default value: 0 for numeric types, false for bool, and null for reference types. Local variables are different: the compiler's definite-assignment rule prevents reading them before they are assigned, so an uninitialized local variable has no usable value until you initialize it.

You can declare and initialize in one step:

int count = 10; string name = "Ada";

The type can be a built-in type, a user-defined class, a struct, an interface, or a generic type. A local variable must be declared before it is used, and the name must follow C# identifier rules.

Using var for Implicit Typing

C# provides the var keyword to declare a variable whose type is inferred from the initializer at compile time. This is not dynamic typing; the compiler determines the exact type and enforces it statically.

var count = 10; // int var name = "Ada"; // string var items = new List<int>(); // List<int>

var is required when the type is anonymous, such as the result of a LINQ Select that returns an anonymous type. It is also convenient when the type name is long and the initializer makes it obvious. However, using var when the type is not clear from the right-hand side can hurt readability.

var result = GetValue(); // What type is result?

In this case, an explicit type is usually better because it documents the contract at the point of declaration.

Declaring Nullable Value Types

Value types like int and bool cannot normally hold null. To allow null, you declare a nullable value type using the ? suffix:

int? maybeCount = null; bool? flag = null;

The Nullable<T> struct underlies this syntax. Accessing the value safely requires checking HasValue or using the ?? operator to provide a default:

int actual = maybeCount ?? 0;

Nullable reference types are a separate feature controlled by the #nullable context, for example #nullable enable. When enabled, the compiler warns if a reference type that is not marked nullable might receive null. You can declare a reference type as nullable with string?:

string? name = null;

This annotation is a compile-time contract that helps prevent NullReferenceException without changing runtime behavior.

Declaration vs Initialization and Default Values

For fields without an initializer, the default value is assigned automatically:

class Example { int _count; // default 0 string? _name; // default null }

For local variables, the compiler requires definite assignment before use. You cannot read a local variable before assigning it:

int x; Console.WriteLine(x); // Compiler error: use of unassigned local variable

You can initialize explicitly with default:

int x = default; // 0 string? s = default; // null

Variable Scope and Lifetime

The scope of a variable is the region of code where it can be referenced. Local variables are scoped to the block in which they are declared, typically a method or a { } block. A variable declared inside a for loop is not accessible outside it.

for (int i = 0; i < 10; i++) { // i is visible here } // i is not visible here

Instance member variables (fields) have class or struct scope and live as long as the containing object. Static fields are shared across all instances of the type. Choosing the right scope is part of designing maintainable code: local variables reduce coupling and make methods easier to reason about.

Maintainability and Readability Tradeoffs

The choice between var and explicit types is a style decision that affects code review and maintenance. Microsoft's C# coding conventions recommend using var when the type is obvious from the initializer and using explicit types when the type is not apparent. There is no performance difference; var is resolved at compile time and produces the same IL as an explicit declaration.

Teams should agree on a convention and apply it consistently. At API boundaries, explicit types are often clearer because they document the contract. Inside method bodies, var can reduce visual noise when the type name is long, such as with nested generics.

Dictionary<string, List<Order>> ordersByCustomer = GetOrders(); var orders = GetOrders(); // type is Dictionary<string, List<Order>>

Both compile to identical code. The decision is about readability, not runtime behavior.

Common Mistakes and How to Avoid Them

One common issue is letting var hide the declared return type of a method. The inferred type is exactly the method's declared return type, so if that type is not what a later operation expects, the compiler reports an error. An explicit type at the declaration can make the intended contract obvious.

Another common issue is forgetting to initialize a local variable and then trying to read it, which the compiler rejects.

When working with nullable value types, check for a value before using .Value, or use ?? to provide a default. The lifted + operator on two int? values returns null if either operand is null:

int? a = 5; int? b = null; int? sum = a + b; // sum is null int total = (a ?? 0) + (b ?? 0); // 5

Trying to use .Value when HasValue is false throws InvalidOperationException:

int bad = a.Value + b.Value; // throws InvalidOperationException

Finally, var cannot be used for a declaration without an initializer. The compiler needs the initializer to infer the type.

var x; // error
C# Variable Declaration: Types, Defaults, and Readability | RYUSLOG DEV