Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A static property in C# is a property declared with the static modifier, binding it to the type itself rather than to any specific instance of that type. Consequently, a static property shares a single state across all instances of a class or struct, and it is accessed directly through the type name rather than through an object reference.
public class ServerConfig
{
    // Auto-implemented static property with an initializer
    public static int MaxConnections { get; set; } = 100;

    // Static backing field
    private static string _environmentName;

    // Static property with explicit accessors
    public static string EnvironmentName
    {
        get => _environmentName;
        set => _environmentName = value;
    }
}

public class Program
{
    public static void Main()
    {
        // Accessing static properties (invoked on the Type, not an instance)
        ServerConfig.MaxConnections = 250;
        string currentEnv = ServerConfig.EnvironmentName;
    }
}

Technical Characteristics

Instance Independence Because static properties do not belong to an object instance, the this keyword is invalid within their get or set accessors. Furthermore, a static property cannot directly access non-static (instance) fields, methods, or properties of its containing type. It can only interact with other static members. Static Classes Static properties are a foundational component of static classes. When a class is declared with the static modifier, it cannot be instantiated, and the C# compiler enforces that all of its members—including properties, fields, and methods—must be explicitly declared as static. Initialization and Static Constructors While static properties can be initialized inline, complex initialization logic, exception handling, or multi-step setup requires a static constructor (static ClassName()). The Common Language Runtime (CLR) guarantees that the static constructor executes in a thread-safe manner exactly once per type before any static members are accessed or any instances are created.
public class DatabaseConfig
{
    public static string ConnectionString { get; }

    // Static constructor
    static DatabaseConfig()
    {
        // Complex initialization logic
        ConnectionString = "Server=myServer;Database=myDataBase;";
    }
}
Behavior in Generic Classes When a static property is declared within a generic class, instances of that generic class share the static state only if they share the exact same type arguments. The C# compiler and CLR generate a completely independent static state and backing field for each closed constructed type.
using System;

public class StateContainer<T>
{
    public static int InitializationCount { get; set; }
}

public class Program
{
    public static void Main()
    {
        StateContainer<int>.InitializationCount = 5;
        StateContainer<string>.InitializationCount = 10;
        
        // Output is 5. The <int> and <string> types maintain isolated static states.
        Console.WriteLine(StateContainer<int>.InitializationCount); 
    }
}
Memory Allocation and Lifecycle The compiler generates a static backing field for auto-implemented static properties. The CLR allocates memory for these static fields in the High Frequency Heap when the type is first loaded into the Application Domain (or AssemblyLoadContext in modern .NET). This memory remains allocated for the lifetime of the application domain, meaning the state persists globally until the application terminates or the context is unloaded. Polymorphism and Inheritance In classes and structs, static properties are resolved at compile-time (early binding) and cannot be marked as virtual, abstract, or override. A derived class hides a base class’s static property by declaring a static property with the same name (member shadowing). This shadowing occurs even if the new keyword is omitted, though omitting it generates a compiler warning (CS0108). Explicitly using the new keyword suppresses this warning. Starting with C# 11, interfaces can declare static abstract and static virtual properties. This feature explicitly enables static polymorphism over types, allowing generic constraints to enforce that a type implements specific static properties.
public interface IConfigurable
{
    // C# 11 static abstract property in an interface
    static abstract string DefaultConfig { get; }
}

public class AppConfig : IConfigurable
{
    // Implementation of the static abstract property
    public static string DefaultConfig => "appsettings.json";
}

public class BaseClass
{
    public static string Status { get; set; } = "Base";
}

public class DerivedClass : BaseClass
{
    // Hides the static property of the base class, suppressing CS0108
    public static new string Status { get; set; } = "Derived";
}
Thread Safety and Synchronization By default, static properties are not thread-safe. Because they represent shared global state, concurrent read/write operations from multiple threads can result in race conditions or data corruption. If a static property is mutable and shared across threads, explicit synchronization is required. When using the lock statement to synchronize access, developers must use a dedicated reference type synchronization object. Attempting to lock a value type backing field results in a compiler error (CS0185). To maintain thread-isolated static properties without the overhead of locking, developers use thread-local storage. This is achieved by applying the [ThreadStatic] attribute to a static backing field or by using the ThreadLocal<T> type, which ensures each thread maintains its own independent state for that static property.
using System.Threading;

public class ThreadSafeConfig
{
    private static int _activeConnections;
    private static readonly object _lockObj = new object();

    // Synchronized shared static property
    public static int ActiveConnections
    {
        get
        {
            lock (_lockObj) { return _activeConnections; }
        }
        set
        {
            lock (_lockObj) { _activeConnections = value; }
        }
    }

    // Thread-isolated static property
    private static readonly ThreadLocal<string> _transactionId = new ThreadLocal<string>();

    public static string TransactionId
    {
        get => _transactionId.Value;
        set => _transactionId.Value = value;
    }
}
Access Modifiers The static modifier dictates binding, not visibility. Static properties can be assigned any standard access modifier (public, private, protected, internal, etc.). A private static property is only accessible to other members (static or instance) within the same class.
Master C# with Deep Grasping Methodology!Learn More