AlgoMaster Logo

static Keyword

Last Updated: May 17, 2026

10 min read

The static keyword attaches a member (field, method, property, or constructor) to the type itself rather than to any instance of it. There's exactly one copy, shared by everything in the program, and you reach it through the type name instead of an object. This lesson covers static fields, methods, properties, full static classes, the brief story of static constructors, the using static directive, and the design question of when to pick static over instance.

What "static" Actually Means

When you create two instances of a class, each instance gets its own set of fields and properties. Two carts have two Total values, two Owner names, two of everything. That's the default and it's what you usually want.

A static member breaks that pattern. There is one copy that belongs to the type. Whether you have zero, one, or a thousand instances of the class, the static member exists exactly once, sitting on the type itself.

Notice how TotalCartsCreated is accessed: Cart.TotalCartsCreated, not alice.TotalCartsCreated. The member lives on the type, so you reach it through the type. In fact, the compiler will warn you if you try to access a static member through an instance, because it implies a relationship that isn't there.

The instance fields Owner and Total belong to each cart. Three carts, three independent Total values. The static field TotalCartsCreated belongs to the Cart type itself. Three carts, still just one counter.

This memory picture is the foundation. Everything else about static is a consequence of it.

The diagram shows the type sitting at the top with one static field hanging off it. Each instance is a separate object with its own Owner and Total. The static field isn't duplicated per instance, it's part of the type's storage. In .NET terms, static fields live in a region of memory associated with the type's loaded metadata, not on the per-instance heap allocation.

Static Fields

A static field holds shared state for the entire type. Use cases include counters (how many orders have been processed), caches (a single lookup table reused across all callers), and configuration that doesn't change per instance (the default tax rate, the current store currency).

Each order increments the shared counter and adds to the shared revenue. No matter how many OrderSystem instances exist, there is one TotalOrdersPlaced and one TotalRevenue. The tax rate is a static default that any instance can read, and any code can update if the rate changes for a sale.

Two practical notes. First, public mutable static fields are dangerous in real code, because anyone can change them from anywhere. Prefer static properties with a private setter, or expose static fields as readonly. Second, static fields live for the lifetime of the application domain. They aren't garbage collected until the program ends, so don't stash large objects in them without thinking.

A safer version using a private static field and a public read-only property:

The field is private, so only the class can change it. Callers read it through the public property. The Interlocked.Increment call replaces _totalOrdersPlaced++ with a thread-safe single operation. You won't always need this level of care, but knowing it exists is the difference between code that works on a developer's laptop and code that works under load.

Static Methods

A static method doesn't operate on a particular instance. It receives all the data it needs through parameters and returns a result. You call it through the type name, with no new required.

CartCalculator is a holder for related functions. None of them need any per-instance state, so making them static is the right call. The BCL Math class is the canonical example: Math.Round, Math.Max, Math.Sqrt, all static, all stateless utilities.

Static methods cannot use this, because there's no instance for this to refer to. They also can't directly access instance fields or instance methods of the enclosing class. They can access other static members, and they can take instances as parameters and call methods on those.

Merge is static because it doesn't belong to one cart; it belongs to the operation of combining two. Putting it as alice.Merge(bob) would feel asymmetric, as if one cart owned the merge and the other was an argument. As a static method, both carts are inputs and the merged result is the output. Clean.

Static factory methods are a common pattern:

The constructor is private, so callers can't say new Product(...). Instead they go through Product.Regular(...) or Product.SaleItem(...), which are named static methods that document the intent. Compare to having a constructor with a bool onSale parameter; the static factory names read better and there's no risk of passing the boolean the wrong way around.

Static Properties

A static property is the property version of a static field. It belongs to the type, you access it through the type name, and it can have any combination of getter and setter (or init-only setter). Static properties are the right way to expose mutable type-level state, because they let you add validation and keep the underlying field private.

TaxRate has a full property with validation in the setter, so callers can't push it outside the legal range. CurrencySymbol is a simple auto-property with a default value. OpenCarts is read-only from outside (private setter), so callers have to go through the OpenCart and CloseCart methods to change it. That's the same pattern you'd use for instance properties; static just means it lives on the type.

Static auto-properties were added in C# 6 and they're the cleanest way to declare simple type-level data. If you don't need validation, you don't need the backing field.

A small but real example: a "current user" property that the rest of the app reads to know who's logged in.

Putting "the current user" on a static property like this is a popular shortcut in small apps. In larger ones it's a known anti-pattern (because it makes testing harder and assumes only one user per process), but knowing the pattern exists and what it looks like matters. Dependency injection replaces it in production code, but you'll still see it in older codebases and in tutorial-sized examples.

Static Classes

When every member of a class is static, you can mark the class itself static. The compiler then enforces a few things: you can't create instances of the class, you can't inherit from it, and every member must be static.

CartMath exists only to group related calculations. There's nothing meaningful about a CartMath instance, so the class is static. Try to write new CartMath() and the compiler stops you with CS0712 (cannot create an instance of the static class).

The rules at a glance:

RestrictionReason
All members must be staticThe class has no instance state, so instance members would be unreachable
Cannot be instantiated (new is disallowed)There's no state to initialize
Cannot be inherited fromA subclass would need its own instance state or the same all-static rule
Cannot inherit (always derives from object)Static-ness must be a leaf property in the hierarchy
Can contain a static constructorFor one-time initialization of static fields
Cannot have instance constructorsThere's no instance to construct

The BCL is full of static classes: Math, Console, File, Directory, Path, Convert, Enumerable (the home of LINQ's extension methods). Each one is a namespace-like grouping of related functions with no instance to attach them to.

Static Constructors

A static constructor runs once, before any instance of the type is created and before any static member is first used. It's the place to set up static state that needs more than a simple field initializer can give you. The full mechanics of static constructors live in the Constructors lesson; here's the short version.

The static constructor doesn't run when the program starts. It runs the first time anything touches the CurrencyTable type, which is when ConvertToEur is called. Subsequent calls don't retrigger it. This lazy, run-once behavior is what makes static constructors useful for setup that's expensive, depends on configuration, or needs to throw on misconfiguration before any real work happens.

A few rules worth knowing without revisiting the dedicated lesson:

  • The static constructor has no access modifier (it's never called by you, only by the runtime).
  • It takes no parameters.
  • A class can have at most one static constructor.
  • It runs exactly once per type, per application domain.

For the full picture (ordering with instance constructors, what happens if it throws, how it interacts with BeforeFieldInit), see the Constructors lesson in this section.

using static Directives

The using static directive (added in C# 6) imports the static members of a type so you can call them without the type qualifier. This is a call-site convenience, not a runtime difference.

Without the using static System.Math;, those calls would have to be Math.Round(...) and Math.Max(...). Without using static System.Console;, they'd be Console.WriteLine(...). The directive shaves off the type prefix.

When does this help? Code that uses one helper class heavily becomes cleaner. A formatting-heavy method that calls Math.Round, Math.Max, Math.Min, Math.Abs a dozen times reads better as Round, Max, Min, Abs. A test file that uses Assert.AreEqual repeatedly gets a similar boost.

When does it hurt? When the unqualified name is ambiguous or vague. Read is less clear than Console.Read. Min could be from Math, from a custom class, or from a LINQ method. If readers have to wonder where a function comes from, you lose more than you save. Use using static for one or two well-known helper types per file, not as a blanket cleanup.

Note: using static is for static members only. You can't use it to import instance members.

When to Use Static vs Instance

The decision between static and instance comes down to one question: does the operation depend on per-instance state? If yes, instance. If no, static. The rest is judgment about what the code is meant to express.

SituationPickWhy
Operation has no state, just inputs and outputsstaticNothing to attach to an instance
Helper functions grouped by topic (math, formatting, conversion)static classThe class is a namespace, not an object
Counter or running total shared across all instancesstatic field/propertyOne copy is the whole point
Factory method that creates instancesstaticThe instance doesn't exist yet
Validation or comparison of two objects of the same typestaticSymmetric, neither object "owns" the operation
Per-object state (cart contents, order details, customer info)instanceEach object's data is independent
Caching shared across the whole appstatic fieldThe cache is a single shared resource
Configuration constantsstatic readonly or constDoesn't change per instance

A simple test: if you find yourself passing the same instance into a method over and over to do the same thing, you might be writing an instance method that wants to be static, or you might be writing a static method that wants to be an instance method. Walk through the code and see which way the dependency points. If the method really only needs one input, static is fine. If it pulls fields off the instance you pass in, it probably belongs on the class as a regular method.

A diagram of the decision flow:

What the diagram is showing. Start from the operation. If it touches per-instance state (reads a field, calls another instance method, mutates the object), it has to be an instance member. If it doesn't, ask whether the type itself has any instances that make sense. A Cart has instances and can host both static helpers and instance methods. A Math-style utility class has no instances at all, so the whole class is static.

A worked example. Consider these three methods on a hypothetical Order class. Which should be static and which shouldn't?

Option A is instance. It changes the state of this. Option B is static and stateless, a pure function from sequence number to an order ID string. Option C is the judgment call. It compares two orders, doesn't favor either one, and would feel awkward as a.IsLargerThan(b) because that implies a owns the comparison. Static reads better here.

Putting It Together

A realistic example combining static fields, static methods, a static factory, a static constructor, and an instance-level cart that uses all of them.

Walk through what each static is doing. The _nextCartId field guarantees unique IDs without coordinating between instances. The TaxRate property is store-wide configuration; changing it once affects every cart's total. The static constructor sets the initial values once. The Create factory hides the constructor and centralizes ID assignment. The instance members (Id, Owner, ItemPrices, AddItem, CalculateTotal) handle per-cart state and behavior. Each piece is on the right side of the static/instance line for what it does.

Summary

  • static attaches a member to the type itself, not to instances. There is one copy, shared by the whole program, accessed through the type name.
  • Static fields hold shared state (counters, caches, configuration). Public mutable static fields are easy to misuse; prefer static properties with private setters for safety.
  • Static methods are functions that don't depend on per-instance state. They can't use this and can't directly access instance members of the enclosing type.
  • Static properties expose type-level data with optional validation. Static auto-properties (C# 6+) are the cleanest way to declare simple type-level state.
  • A static class is one where the compiler enforces that all members are static, no instances can be created, and no inheritance is allowed. Use it when the class is a namespace-like grouping of helpers (Math, Console, Path).
  • Static constructors run once, automatically, before any first use of the type. They initialize static state that's too complex for a field initializer. Details live in the Constructors lesson.
  • using static SomeType; imports a type's static members so you can call them without the type prefix. Useful for one or two well-known helpers per file; overuse hurts readability.
  • The static-vs-instance choice is decided by state: if the operation touches per-instance data, instance; otherwise, static. Pure utility classes with no state at all should be marked static class.