Constance

When I was first learning to program, one of the fundamental things that every introductory language course covered was the idea of a "constant". I never thought too much about them at first, because the idea seemed so simple and obvious. A thing that is constant does not change. So a constant variable is one that does not change. Easy. What's next?

In the courses, what's next was invariably something like functions or pointers or string interpolation. But I think new programmers might be better served instead putting a follow-up question next.

What does it mean for a variable to "change"?

The C++ language, as an example, embraces the ambiguity of the question and tries to answer all interpretations. Consider a pointer to an object type value. You can change the pointer address, or replace the value, or you can mutate bits of the value itself. C++ allows you to lock down all of those kinds of change independently, even down to individual fields internal to the object.

C# walked away from this madness and made things very simple, but still not very intuitive for newbies. C# has two different kinds of invariable variables: "const" and "readonly". How can we understand them as easily as possible? We ask, "What does change mean?"

With regards to "readonly" and "const" in C#, change means simply to be assigned different instances over time. And the distinction between readonly and const is at what point in the life of the variable this change becomes prohibited.

A readonly field is one whose value cannot be reassigned after it is initialized. Initialization can happen at the point of definition, or in a constructor. Local variables cannot be readonly. I haven't seen a reason for this except that the CLR doesn't have this feature, and the benefit to adding it at the language level was low.

public class A {
    readonly List<int> _one = new List<int> { 1, 2, 3 };
    readonly List<int> _two;
    
    public A() {
        _two = new List<int> { 4, 5, 6 };
    }
    
    public void DoSomething() {
        _one = new List<int>(); // won't compile
        _two = new List<int>(); // won't compile
    }
}

A const variable is one whose value cannot be reassigned after the program is compiled. This has a couple of natural consequences that are surprising unless you start from this definition. At compilation you cannot construct complicated objects or assign references, you can only assign primitive literals and nulls. And while local variables can be const, fields that are const are implicitly also made static. Why? Because it saves memory, and there's no reason for them not to be.

public class A { 
    const List<int> _one = new List<int> { 1, 2, 3 }; // won't compile
    const int _two = 1;
    
    public int DoSomething() {
        return A._two; // static!
    }
    
    public int DoSomethingElse() {
        const int three = 2;
        
        three = 3; // won't compile
        _two = 2; // won't compile
        
        return three;
    }
}