可変型のフィールドにreadonlyをつけてはいけないのか?
X DO NOT assign instances of mutable types to readonly fields. A mutable type is a type with instances that can be modified after they are instantiated. For example, arrays, most collections, and streams are mutable types, but System.Int32, System.Uri, and System.String are all immutable. The read-only modifier on a reference type field prevents the instance stored in the field from being replaced, but it does not prevent the field’s instance data from being modified by calling members changing the instance.
An externally visible type contains an externally visible read-only field that is a mutable reference type. The read-only modifier (readonly in C#, ReadOnly in Visual Basic, and const in C++) on a reference type field (or pointer in C++) prevents the field from being replaced by a different instance of the reference type. However, the modifier does not prevent the instance data of the field from being modified through the reference type.
CA2104:読み取り専用の変更可能な参照型を宣言しません - Visual Studio | Microsoft Docs
readonly付ければ再代入は防げるけど、それ自体が可変型だったら書き換えれてしまうからダメだよ、とのこと。これが妥当なガイドラインだとは思えない。後者はprivate以外のフィールドに限った警告なので若干理解できるが…。
readonlyが付いているフィールドにはその型のコンストラクタを抜けた後は代入されないことを保証できる。単にそれだけだ。可変型だったら書き換えられるというのは当たり前で、それはreadonlyとは別の話。可変か不変かという話とreadonlyの話を混ぜてはいけない。
- 不変型を再代入不可なフィールドで扱いたい
- 不変型を再代入可能なフィールドで扱いたい
- 可変型を再代入不可なフィールドで扱いたい
- 可変型を再代入可能なフィールドで扱いたい
これらは全てありうる。
例えば以下のように書くと、誤って型内部でListを再代入してしまうことを防げる。型内部でListが可変なのは誰が見ても分かるし、一方で型外部から見てもIReadOnlyListの中身が可変型である可能性は明らかであるので、メリットしかない。再代入を意図していないフィールドには全てreadonlyをつけておいた方が設計意図が明確になるのではないだろうか。