Difference Between const, readonly, and static in C# (With Examples)

If you’ve spent any time working in C#, you’ve likely come across the keywords const, readonly, and static. They look similar at first glance — all three relate to variables that don’t seem to change much. But under the hood, they work differently and serve distinct purposes.

In this article, we’ll break down the differences between const, readonly, and static in C#, show practical examples, and highlight common mistakes developers make with each one. By the end, you’ll know exactly when to use each keyword in your own code.

What Is const in C#?

A const (constant) is a value that is known at compile time and cannot be changed afterward.

Key Points

  • Must be initialized at the time of declaration.

  • Value is replaced directly into the compiled code (inlined).

  • Only certain types can be const: numbers, bool, char, string, and enums.

  • Implicitly static (no need to mark it as static).

Example

const is meant for values known at compile time and never meant to change

When to Use const

  • For values that are guaranteed to never change.

  • For mathematical constants, fixed strings, or settings that never vary.

  • When performance matters (compiler inlining makes lookup extremely fast).

What Is readonly in C#?

A readonly field can only be assigned in two places:

  1. When it’s declared.

  2. In the class constructor.

Unlike const, a readonly value is resolved at runtime, not compile time.

Example

readonly is for data that needs to be determined at runtime, but will not change afterwards

When to Use readonly

  • When the value is fixed after object construction but not known at compile time.

  • For values that depend on runtime logic (environment, configuration, GUIDs, etc.).

What Is static in C#?

The static keyword means that a member belongs to the type itself, not an instance.

Key Points

  • A static field is shared across all instances of a class.

  • A static class cannot be instantiated — it can only contain static members.

  • Static methods cannot access instance members unless passed an object.

Example

static data belongs to the type itself- not to each instance made from the type

When to Use static

  • When you need shared state across all objects.

  • For utility/helper methods (e.g., Math.Sqrt()).

  • To implement singletons or application-wide resources.

Putting It All Together

An example using const, readonly, and static

The following table summarizes what we’ve learned. The “instance or type” row denotes whether the data belongs to the type itself or whether instances constructed from that type each receive their own version of the data.

Summarizing the differences between const, readonly, and static

Performance Notes

One of the subtler differences between these keywords lies in performance and compilation behavior.

  • const values are inlined
    When you use a const, the compiler literally replaces the symbol with the constant value in the compiled IL. This makes lookups extremely fast because there’s no memory indirection — it’s just the value itself. The downside is that changing a const requires recompiling every assembly that depends on it.

  • readonly fields require a lookup
    A readonly field is stored as part of the object (or type, if declared static readonly). Accessing it means retrieving a memory reference at runtime. This adds a tiny cost, but it’s usually negligible compared to the safety and flexibility you gain.

  • static fields are shared in memory
    Static fields are created once per application domain and live for the lifetime of the process. This can be more efficient than duplicating values across instances, but it also means you need to be careful about concurrency, as multiple threads may access the same field.

Rule of thumb:

  • Use const when performance is critical and the value will never change.

  • Use readonly when correctness and maintainability matter more than shaving nanoseconds.

  • Use static when you want to reduce duplication or provide global access.

Common Pitfalls

Here’s where things often go wrong.

1. Confusing const and readonly

Developers sometimes use const for values that might change between builds (like API endpoints). This can lead to hard-to-track bugs because consumers compiled against the old constant won’t see the update.

👉 Use readonly instead for anything that isn’t truly universal and permanent.

2. Using static for convenience

It’s tempting to make everything static for easy access. But static variables behave like global state. They persist across the entire application, can be modified from anywhere, and can introduce subtle bugs in multithreaded environments.

👉 Avoid overusing static for things like configuration. Instead, use dependency injection or pass objects explicitly.

3. Thread safety issues with static fields

Static fields are shared, so if multiple threads read/write to them, you can run into race conditions. For example, incrementing a static counter without synchronization can lead to inconsistent results.

👉 If you must use shared state, combine static with lock, Interlocked, or immutable data structures.

4. Over-optimizing for performance

Some developers default to const because “it’s faster.” While true in theory, the performance difference is negligible in most apps. Choosing the wrong keyword for semantic reasons (e.g., making something const when it should be readonly) can cause long-term maintainability issues that far outweigh any speed gain.

👉 Use the keyword that expresses your intent clearly. The compiler and JIT are very good at optimizing.

5. Forgetting that const is implicitly static

Beginners sometimes try to combine static with const:

It’s redundant to add the static qualifier to const

const is already implicitly static, so this is unnecessary and won’t compile.

6. Unintended object lifetimes with static references

If you hold large objects in static fields, they’ll live for the duration of the app and won’t be garbage collected. This can lead to memory leaks.

👉 Be intentional when using static with reference types. If the data is temporary, don’t make it static.

Conclusion

While const, readonly, and static look similar, they solve very different problems:

  • const → compile-time constants, inlined into code, never change.

  • readonly → runtime constants, set once at construction, safer for evolving values.

  • static → shared across all instances, useful for utilities and global state.

The choice isn’t just about syntax — it’s about performance, maintainability, and correctness. const can give you tiny speed benefits, but readonly offers flexibility. static can cut down on duplication but risks introducing global-state bugs.

By mastering these keywords — and understanding both their power and pitfalls — you’ll avoid subtle bugs, write clearer code, and know exactly when to reach for each tool.

Previous
Previous

What Are Assemblies in C#: A Complete Beginner’s Guide

Next
Next

10 Lesser-Known C# Operators That Will Surprise You (With Examples)