C#: Difference between Const and Readonly

A thumbnail showing C# Const and Readonly code.

Const vs Readonly

The main difference between const and readonly keywords in C# is that const need to be defined at the time of assignment, while readonly field can be defined at runtime. Const's are implicitly static, while readonly values don't have to be static.

Moreover, const and readonly have different behavior when working with assemblies. Constants are copied into every assembly that uses them, while readonly fields are shared across assemblies.

Constants don't have memory allocation during runtime because they are embedded into the IL code. On the other hand, readonly fields allocate memory because they are stored in the loader heap.

Differences between const and readonly
ConstReadonly
Must be defined at compile timeCan be defined at compile time or runtime
Implicitly staticInstance-level or static
Copied across assembliesShared across assemblies
Do not allocate memoryAllocate memory

Assignment

Unlike with constants, we can determine the value of a readonly field at runtime but only within the scope of a constructor.

csharp
public class App
{
    public readonly int VERSION = 5;
    public App()
    {
        VERSION = 6; // This works!
    }
    public void UpdateVersion()
    {
        VERSION = 7; // This does not work!
    }
}

On the other hand, constants cannot change once defined. For example:

csharp
public class App
{
    public const int VERSION = 5;
    public App()
    {
        VERSION = 6;
    }
}

If we try to run the code above, we will get the error:

Error: (6,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer

Assemblies

Const and readonly also have different behavior when working with assemblies. A static readonly field is shared across assemblies, while const value is baked into each assembly.

For example, lets say we have assembly A and assembly B.

Assembly A defines the constant:

csharp
public const int PRODUCTS_PER_PAGE = 10;

Both assembly A and assembly B use the same constant.

If we change the constant above to be 15, we need to recompile both assemblies.

On the other hand, with static readonly field, we would only need to recompile the assembly A.

Because of sharing across assemblies, static readonly properties are a great to speed up string comparison, as I demonstrated in my string enum alternatives article.

Use const for values that you truly know will never change. For example, Math PI value.

FAQ on const vs readonly differences

Can local variable be readonly?

A local variable cannot be readonly. The readonly keyword can only be used on fields, not on local variables.

How to fix "A readonly field cannot be assigned to" error?

If you try to change the value of a readonly field outside of the constructor, you will get the following error:

error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer)

To fix "A readonly field cannot be assigned to" error, remove the readonly keyword from your field.

But be careful because doing so will impact encapsulation. Your field will become accessible from outside the class. If you don't want to do that, a better approach would be to keep the readonly modifier and to change the design so the value can only be changed from within the constructor.

Before:

csharp
public class App
{
    public int VERSION = 5;
    public App()
    {
    }
    public void UpdateVersion(int version)
    {
        VERSION = version;
    }
}

After:

csharp
public class App
{
    public readonly int VERSION = 5;
    public App(int version)
    {
        VERSION = version;
    }
    public void UpdateVersion(int version)
    {
        VERSION = version;
    }
}

Is it safe to use readonly with .NET dependency injection?

Yes, we can use readonly fields with .NET dependency injection.

Here's an example of such behavior with dependency injection in .NET:

csharp
class OrderService {
  private readonly CartService _cartService;
  public OrderService(CartService cartService) {
    _cartService = cartService;
  }
}

The OrderService above has a dependency of CartService which is unknown until the runtime. We use private readonly field for the CartService because it's passed via OrderService constructor.

Published on