Static members belong to the type itself, not to any instance.
Imagine you're building a system that tracks how many users are currently logged in. You need a single counter that every part of your application can access and update — but you don't want to create an object just to hold that counter.
That's where static members come in. They belong to the class itself, not to any particular object. Static fields hold data that is shared across all instances, and static methods provide utility functions that don't depend on object state.
In this lesson, you'll learn about static fields, properties, methods, constructors, and classes — and when to use them in your applications.
A static member is a field, property, method, or constructor that belongs to the class itself, not to any specific object. You access it using the class name, not an instance variable.
Instance members — each object has its own copy (e.g., person.Name).
Static members — shared across all objects (e.g., Person.TotalCount).
Static members exist even if no objects of the class have been created.
A static member is declared with the static keyword. It is associated with the type, not with instances. Static members are stored in a special area of memory that is shared across all instances and accessed via the type name.
| Member Type | Keyword | Access | Example |
|---|---|---|---|
| Instance field | — | obj.Name |
Each object has its own |
| Static field | static |
ClassName.Value |
Shared across all objects |
| Static method | static |
ClassName.Method() |
Utility / helper functions |
| Static constructor | static |
— | Runs once per type |
| Static class | static class |
ClassName.Method() |
Contains only static members |
Sometimes you need data or functionality that isn't tied to a specific object. For example:
Without static members, you'd have to create a dummy object just to hold such data or methods — which is awkward and inefficient.
Static members provide a clean way to:
Math.Sqrt are available anywhere.Here's how static members relate to the type and its instances:
Let's explore different types of static members with examples.
public class Counter
{
public static int TotalInstances = 0; // static field
public int InstanceNumber { get; }
public Counter()
{
TotalInstances++;
InstanceNumber = TotalInstances;
}
}
var c1 = new Counter();
var c2 = new Counter();
var c3 = new Counter();
Console.WriteLine(Counter.TotalInstances); // 3
Console.WriteLine(c1.InstanceNumber); // 1
Console.WriteLine(c2.InstanceNumber); // 2
Console.WriteLine(c3.InstanceNumber); // 3TotalInstances is shared across all objects. Each constructor increments it.
public class Configuration
{
private static string _environment = "Development";
public static string Environment
{
get => _environment;
set
{
if (value == "Production" || value == "Development" || value == "Staging")
_environment = value;
else
throw new ArgumentException("Invalid environment.");
}
}
}
Console.WriteLine(Configuration.Environment); // Development
Configuration.Environment = "Production";
Console.WriteLine(Configuration.Environment); // ProductionStatic properties allow validation and logic when accessing static data.
public class MathHelper
{
// Static utility method
public static int Add(int a, int b) => a + b;
// Static factory method
public static Counter CreateCounter()
{
return new Counter();
}
}
int sum = MathHelper.Add(5, 3); // 8
var counter = MathHelper.CreateCounter();Static methods are called on the class itself. They don't have a this reference.
public class Database
{
private static string ConnectionString;
static Database()
{
// Runs once when the type is first accessed
ConnectionString = LoadFromConfig();
Console.WriteLine("Static constructor ran.");
}
private static string LoadFromConfig() => "Server=localhost;Db=MyDb;";
public static void Connect()
{
Console.WriteLine($"Connecting with {ConnectionString}");
}
}
Database.Connect(); // Static constructor runs firstStatic constructors are perfect for loading configuration, initialising static fields, etc.
public static class StringExtensions
{
public static string Reverse(this string input) // extension method
{
return new string(input.Reverse().ToArray());
}
public static bool IsNullOrWhitespace(string input)
{
return string.IsNullOrWhiteSpace(input);
}
}
string reversed = "hello".Reverse(); // "olleh" (using extension method)
bool empty = StringExtensions.IsNullOrWhitespace(" "); // trueStatic classes cannot be instantiated or inherited. They're ideal for utility libraries.
Let's build a Library system that tracks total books and provides utility methods.
public class Book
{
// ─── Static members ───
private static int _nextId = 100;
private static int _totalBooks = 0;
public static int TotalBooks => _totalBooks;
// Static constructor: runs once
static Book()
{
Console.WriteLine("Book static constructor: Initialising.");
}
// Static factory method
public static Book CreateBook(string title, string author)
{
return new Book(title, author);
}
// ─── Instance members ───
public int Id { get; }
public string Title { get; }
public string Author { get; }
private Book(string title, string author)
{
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title required.");
if (string.IsNullOrWhiteSpace(author)) throw new ArgumentException("Author required.");
Id = Interlocked.Increment(ref _nextId);
Title = title;
Author = author;
_totalBooks++;
}
public void Display()
{
Console.WriteLine($"#{Id}: {Title} by {Author}");
}
}
// ─── Static class for utilities ───
public static class BookUtilities
{
public static string FormatTitle(string title)
{
return title.ToUpperInvariant();
}
public static bool IsValidIsbn(string isbn)
{
return isbn?.Length == 13 && isbn.All(char.IsDigit);
}
}
// ─── Usage ───
var book1 = Book.CreateBook("1984", "George Orwell");
var book2 = Book.CreateBook("Brave New World", "Aldous Huxley");
book1.Display(); // #101: 1984 by George Orwell
book2.Display(); // #102: Brave New World by Aldous Huxley
Console.WriteLine($"Total books: {Book.TotalBooks}"); // 2
Console.WriteLine(BookUtilities.FormatTitle("hello")); // HELLO
Console.WriteLine(BookUtilities.IsValidIsbn("9781234567890")); // TrueKey takeaways from this example:
_totalBooks tracks the total number of books created.CreateBook provides a controlled way to create instances.BookUtilities holds pure utility functions.In a typical application, static members are used for logging, configuration, and the singleton pattern.
// ─── Singleton using static members ───
public class AppConfig
{
private static AppConfig? _instance;
private static readonly object _lock = new();
private AppConfig() { } // Private constructor prevents external instantiation
public static AppConfig Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance ??= new AppConfig();
}
}
return _instance;
}
}
public string Environment { get; set; } = "Development";
public int MaxRetries { get; set; } = 3;
}
// ─── Static logger ───
public static class Logger
{
private static readonly string _logFilePath;
static Logger()
{
_logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "app.log");
Console.WriteLine($"Logger initialised. Log file: {_logFilePath}");
}
public static void LogInfo(string message)
{
var entry = $"[INFO] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}";
Console.WriteLine(entry);
// In a real app, you'd write to a file.
}
public static void LogError(string message, Exception? ex = null)
{
var entry = $"[ERROR] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}" + (ex != null ? $"\n{ex}" : "");
Console.WriteLine(entry);
}
}
// ─── Usage in a real application ───
var config = AppConfig.Instance;
Logger.LogInfo($"Application starting in {config.Environment} mode.");
Logger.LogInfo($"Max retries: {config.MaxRetries}");
try
{
// Simulate some work
throw new InvalidOperationException("Something went wrong.");
}
catch (Exception ex)
{
Logger.LogError("An error occurred.", ex);
}This example shows:
Instance ensures only one configuration object exists.Logger is a static class with methods available everywhere.Think of a class as a company. The company itself has attributes that are shared by all employees: the company name, the address, the number of employees.
Each employee (object) has their own attributes: name, salary, employee ID.
Static members are like the company's central resources; instance members are like each employee's personal belongings.
How does the .NET runtime handle static members?
AppDomain.this pointer, so they can't access instance members directly.abstract sealed in IL — it cannot be instantiated or inherited.Use instance members when the data or behaviour depends on the specific object's state. Use static members when the data or behaviour is common to all objects or doesn't require any object state.
A static method cannot directly access instance fields or methods because it doesn't have a this reference. If you need to access instance data, you must pass an instance reference as a parameter.
public class Example
{
private int _value;
public static void StaticMethod(Example obj) // pass instance
{
Console.WriteLine(obj._value);
}
}An instance constructor runs each time an object is created. A static constructor runs once, before any static member is accessed or any instance is created. Static constructors cannot be called explicitly and have no parameters.
Wrong:
public class User
{
public static string Name { get; set; } // All users share the same name — bad!
}Correct:
public class User
{
public string Name { get; set; } // Each user has their own name
}Wrong:
public static void PrintName()
{
Console.WriteLine(Name); // Error: Can't access instance field Name
}Correct (pass an instance):
public static void PrintName(User user)
{
Console.WriteLine(user.Name);
}If you have static fields accessed from multiple threads, you must synchronise access (e.g., using locks).
private static int _counter;
public static void Increment()
{
Interlocked.Increment(ref _counter); // Thread-safe increment
}Static classes make code harder to test and can create hidden dependencies. In modern applications, prefer dependency injection over static utilities for shared services.
Math.Sqrt).thisClassName.MemberYou've seen how static members work. Let's test your knowledge.
1. Which of the following statements about static members is true?
Correct: C
Why C is correct: Static members belong to the class itself, not to any instance. They are shared across all objects and exist even when no objects are created.
Why A is incorrect: Static members are accessed using the class name, not an instance.
Why B is incorrect: Static members are not per-object; they are per-type.
Why D is incorrect: Static members exist even without any instances. They can be accessed at any time.
Reinforcement: Static = type-level, shared; instance = object-level, separate.
2. Can a static method access an instance field directly?
Correct: C
Why C is correct: Static methods do not have a this reference, so they cannot access instance members directly. They would need an instance reference passed as a parameter.
Why A is incorrect: Static methods cannot access instance fields directly.
Why B is incorrect: Even if the field is public, the static method still lacks a specific instance to access it.
Why D is incorrect: Being in the same class doesn't give the static method a this reference.
Reinforcement: Static methods operate at the type level; instance members require an object.
3. When does a static constructor run?
Correct: B
Why B is correct: A static constructor runs automatically once, before any static member is accessed or any instance of the type is created. It is called by the runtime when the type is first referenced.
Why A is incorrect: Static constructors run once, not per instance.
Why C is incorrect: They run at initialisation, not at termination.
Why D is incorrect: You cannot call a static constructor explicitly; it's invoked by the runtime.
Reinforcement: Static constructors are perfect for one-time setup, like loading configuration.
4. Which of the following correctly declares a static class?
Correct: A
Why A is correct: A static class is declared with the static keyword. It cannot be instantiated and can only contain static members.
Why B is incorrect: This is a regular class with a static method, not a static class.
Why C is incorrect: sealed prevents inheritance but doesn't make it static.
Why D is incorrect: abstract classes can have instance members and can be inherited.
Reinforcement: Static classes are containers for utility methods and constants.
5. What is the output of the following code?
public class Test
{
public static int Count = 0;
public int InstanceNumber;
public Test()
{
Count++;
InstanceNumber = Count;
}
public static void ResetCount() => Count = 0;
}
var t1 = new Test();
var t2 = new Test();
Test.ResetCount();
var t3 = new Test();
Console.WriteLine($"{t1.InstanceNumber} {t2.InstanceNumber} {t3.InstanceNumber}"); Correct: A — 1 2 1
Why A is correct: Count is static and shared. After two objects, Count = 2. ResetCount() sets Count = 0. Then t3 is created, incrementing Count to 1, so t3.InstanceNumber = 1. t1 and t2 have their numbers already set (1 and 2). So output is "1 2 1".
Why B is incorrect: This would happen if ResetCount wasn't called.
Why C is incorrect: This would be if the reset didn't happen and counter started at a different base.
Why D is incorrect: This would be if instance numbers were not set correctly.
Reinforcement: Static fields are shared; changes affect all objects. Instance fields capture the value at creation time.
You now understand static members — the type-level features that provide shared state and utility in C#!
dotnetmadeeasy.com — Learn C# and .NET, the right way.