Skip to content

C# - Variables

A variable is a named location in memory used to store data. In C#, every variable has a specific type, which determines the size and layout of the variable’s memory, the range of values that can be stored, and the set of operations that can be applied to it.

Before you can use a variable, you must declare it by specifying its type and name. You can also initialize it (assign a value) at the same time.

<type> <variableName> = <initialValue>;
// Declaring and initializing variables
string name = "Alice";
int score = 95;
bool isActive = true;
decimal price = 19.99m; // 'm' suffix for decimal literals

Implicitly-Typed Variables: The var Keyword

Section titled “Implicitly-Typed Variables: The var Keyword”

Modern C# allows you to use the var keyword to declare a variable without explicitly stating its type. The compiler infers the type from the value assigned on the right-hand side. This can make code more concise.

Best Practice: Use var when the type is obvious from the right side of the assignment (e.g., var user = new User();). If the type is not obvious or if it enhances clarity, use the explicit type name (e.g., int totalCount = GetCount();).

// Using 'var' for implicit typing
var customerName = "Bob"; // Compiler infers 'string'
var orderCount = 10; // Compiler infers 'int'
var product = new Product(); // Compiler infers 'Product' class

In C#, types are categorized based on how they store their data in memory:

Value types directly contain their data. When a value type variable is assigned to another, the value is copied. They are typically stored on the stack, which is fast for allocation and cleanup.

  • Integral types: int, long, byte, short
  • Floating-point types: float, double, decimal
  • Other types: bool, char, structs, enums
int a = 10;
int b = a; // The value 10 is COPIED to b
b = 20; // Changing b does NOT affect a
Console.WriteLine($"a = {a}, b = {b}"); // Output: a = 10, b = 20

Reference types store a reference (or memory address) to where the actual data is located on the heap. When a reference type variable is assigned to another, only the reference is copied, not the object itself. Both variables then point to the same object.

  • Built-in types: string, object
  • User-defined types: class, interface, delegate
  • Collections: arrays (e.g., int[]), List<T>
int[] arrayA = { 1, 2, 3 };
int[] arrayB = arrayA; // The REFERENCE is copied. Both point to the same array.
arrayB[0] = 99; // Modifying the array through arrayB AFFECTS arrayA
Console.WriteLine($"arrayA[0] = {arrayA[0]}"); // Output: arrayA[0] = 99
  • const: The value is fixed at compile-time and cannot be changed. It is implicitly static. Use for true universal constants like Math.PI.
  • readonly: The value can be assigned at declaration or in a constructor. It becomes immutable after the object is created. Use for instance-specific values that shouldn’t change during the object’s lifetime.
public class Config
{
public const int MaxRetries = 3; // Compile-time constant
public readonly string ConnectionId; // Runtime constant
public Config(Guid id)
{
ConnectionId = $"conn_{id}"; // Can only be set here
}
}

Modern C# puts a strong emphasis on null safety to prevent the infamous NullReferenceException. You can make value types nullable by adding a ?. For reference types, modern projects enable nullable checks by default, forcing you to handle potential null values.

#nullable enable // Enable nullable reference type checking
// Nullable value type
int? optionalValue = null;
if (optionalValue.HasValue)
{
Console.WriteLine($"Value is: {optionalValue.Value}");
}
// Nullable reference type
string? message = null; // The '?' indicates this string can be null
// Console.WriteLine(message.Length); // Compiler gives a warning here!
if (message != null)
{
Console.WriteLine(message.Length); // Safe to use within this block
}
  • Naming Conventions: Use camelCase for local variables (int userAge;) and PascalCase for public properties and fields (public string UserName;).
  • Descriptive Names: Choose names that clearly describe the variable’s purpose. customerAddress is better than adr.
  • Minimize Scope: Declare variables in the smallest possible scope where they are needed. This improves readability and reduces bugs.