Not too long ago the proposal for read-only local variables was revived. This is a much more modest feature than the read-only references proposal, but the two are complementary.
The basic syntax for read-only local variables is to simply prepend the type name with the readonly
keyword. This would work in local variables essentially in the same way it works for fields. You would also be able to apply the readonly
keyword to parameters.
As a short cut, you can use let
in lieu of readonly var
. The keyword let
was chosen for several reasons:
- It is only a keyword in C# LINQ expressions for read-only range variables
- It is also the keyword in F# (and other languages) for declaring read-only local variables
- It is more visually distinct than
val
, another popular keyword for this role
The basic use case for this is informational; it tells the person reading the code that the local variable won’t change once set.
The proposal notes that it is also useful for defensive coding when using anonymous or asynchronous functions. A common mistake is to create a closure over a local variable, then pass that closure to a different thread. This causes a hard to detect race condition, as most developers don’t think about race conditions as even being possible for local variables. By marking the variable as read-only, the compiler will prevent reassignment in the closure.
You would not be able to pass a read-only variable to a function as a ref
or out
argument. You could, however, pass it as a readonly ref
argument if that proposal is also implemented.
Warning: if you declare a struct variable, local or parameter as read-only, then you cannot call methods on it without the compiler implicitly making a copy. For small structs such as integers, this isn’t an issue, but it can be problematic for large structs with non-trivial copying costs. See “readonly structs” in the read-only references proposal for a possible fix.
Open Design Questions
While let x
would require immediate assignment, there is an open question as to if readonly Type x
would as well.
The argument for it is that requiring assignment at declaration makes it easier to see what that assignment was. Plus, it makes the overall proposal simpler.
The argument against requiring immediate assignment is likewise deals with readability. There are times when using conditional notation (x = a ? b : c)
makes the code harder to read. And it wouldn’t work at all if you needed to support a try-catch block.
More Information