I am trying to understand what use cases would require me to declare a List<string> as a ReadOnly type.
An associated question with this is: How much memory upon instantiation of a list gets allocated?
In a field declaration, readonly indicates that assignment to the field can only occur as part of the declaration or in a constructor in the same class. A readonly field can be assigned and reassigned multiple times within the field declaration and constructor.
In C#, you can use a readonly keyword to declare a readonly variable. This readonly keyword shows that you can assign the variable only when you declare a variable or in a constructor of the same class in which it is declared.
If it's private and readonly , the benefit is that you can't inadvertently change it from another part of that class after it is initialized. The readonly modifier ensures the field can only be given a value during its initialization or in its class constructor.
The main reason to mark a field as readonly
is so that you know that regular code cannot have swapped the list reference. One key scenario where that might matter is if you have other code in the type that is performing synchronization against the list using a lock(theListField)
. Obviously if someone swaps the list instance: things will break. Note that in most types that have a list/collection, it isn't expected to change the instance, so this readonly
asserts that expectation. A common pattern is:
private List<Foo> _items = new List<Foo>();
public List<Foo> Items => _items;
or:
public List<Foo> Items {get;} = new List<Foo>();
In the first example, it should be perfectly fine to mark that field as readonly
:
private readonly List<Foo> _items = new List<Foo>();
Marking a field as readonly
has no impact on allocations etc. It also doesn't make the list read-only: just the field. You can still Add()
/ Remove()
/ Clear()
etc. The only thing you can't do is change the list instance to be a completely different list instance; you can, of course, still completely change the contents. And read-only is a lie anyway: reflection and unsafe code can modify the value of a readonly
field.
There is one scenario where readonly
can have a negative impact, and that relates to large struct
fields and calling methods on them. If the field is readonly
, the compiler copies the struct onto the stack before calling the method - rather than executing the method in-place in the field; ldfld
+ stloc
+ ldloca
(if the field is readonly
) vs ldflda
(if it isn't marked readonly
); this is because the compiler can't trust the method not to mutate the value. It can't even check whether all the fields on the struct are readonly
, because that isn't enough: a struct
method can rewrite this
:
struct EvilStruct
{
readonly int _id;
public EvilStruct(int id) { _id = id; }
public void EvilMethod() { this = new EvilStruct(_id + 1); }
}
Because the compiler is trying to enforce the readonly
nature of a field, if you have:
readonly EvilStruct _foo;
//...
_foo.EvilMethod();
it wants to ensure that the EvilMethod()
can't overwrite _foo
with a new value. Hence the gymnastics and the copy on the stack. Usually this has negligible impact, but if the struct is atypically large, then this can cause a performance problem. The same issue of guaranteeing that the value doesn't change also applies to the new in
argument modifier in C# 7.2:
void(in EvilStruct value) {...}
where the caller wants to guarantee that it doesn't change the value (this is actually a ref EvilStruct
, so changes would be propagated).
This issue is resolved in C# 7.2 by the addition of the readonly struct
syntax - this tells the compiler that it is safe to invoke the method in-situ without having to make the extra stack copy:
readonly struct EvilStruct
{
readonly int _id;
public EvilStruct(int id) { _id = id; }
// the following method no longer compiles:
// CS1604 Cannot assign to 'this' because it is read-only
public void EvilMethod() { this = new EvilStruct(_id + 1); }
}
This entire scenario doesn't apply to List<T>
, because that is a reference type, not a value type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With