po co się robi taki kostruktor z polem tylko do odczytu z innej klasy
Generalnie idea modyfikatora readonly jest dość prosta. Chodzi o dwie kwestie (ale być może są jeszcze inne zalety które przez nieuwagę pominąłem):
- Łatwiej taki kod się czyta. Co to znaczy? Chodzi o to, że widząc deklaracje private readonly Foo bar; wiesz i masz gwarancję[1], że te pole nie zostanie nadpisane nigdzie poza konstruktorem. To znaczy, że gdzieś w środku kodu e innej metodzie nie będzie przypisania typu np. bar = new Foo(); i to masz zagwarantowane poprzez kompilator - inaczej taki kod się nie skompiluje. Dokładnie takie samo zadanie daje private - też daje pewne gwarancje od strony kompilatora.
- Można precyzyjniej wyrazić intencje użycia danego pola. Co mam na myśli przez słowo intencje? To stosunkowo proste. Np. int a; wyraża intencje, że ta zmienna będzie przechowywać liczby (int) a nie stringi lub inne obiekty; z drugiej strony object a; wyraża, że zmienna a może zawierać każdy obiekt i trudno jest stwierdzić jaki - trzeba przeczytać resztę kodu aby się dowiedzieć. Tak samo jak private wyraża, że ta zmienna nie powinna być widoczna bezpośrednio na zewnątrz danej klasy. Tak samo readonly mówi "te pole nie będzie nadpisane w żadnej metodzie tej klasy - przypisanie pola będzie możliwe tylko i wyłącznie w konstruktorze (lub podczas konstrukcji obiektu)"
- być może (podkreślam, być może) daje to większe szanse kompilatorowi na optymalizację (w końcu kompilator dostaje więcej informacji na temat użycia danego pola). Osobiście w to śmiem wątpić - ale może kompilator robi z tego jakiś użytek - ale to kwestia do sprawdzenia.
Dwa powyższe punkty nieco się przenikają ale taki jest ogólny sens tego modyfikatora.
Gdy pole nie ma modyfikatora readonly to nie ma tego komfortu podczas czytania (czy nawet pisania!) kodu - aby dowiedzieć się, czy pole te jest nadpisywane, musisz prześledzić wszystkie użycia danego pola manualnie (nie zrobi tego za Ciebie kompilator). W przeciwnym wypadku (jeśli nie prześledzisz użyć) nie możesz być pewny, że to pole nie jest nadpisywane gdziekolwiek indziej w obrębie danej klasy.
W wielu przypadkach ma to niebagatelne znaczenie w przypadku debuggowania/refactoringu czy po prostu próby zrozumienia danego kodu - po prostu lepiej i szybciej można zrozumieć w jaki sposób dane pole jest używane. Nie zawsze jest to kluczowe, ale bardzo często ułatwia to pracę.
Generalnie warto dodawać ten modyfikator jeśli z założenia pewnych pól nie będziemy chcieli nadpisywać. Ułatwia to czytanie i utrzymanie takiego kodziku.
[1] W sumie to gwarancji nigdy nie ma, bo jest refleksja, ale w typowym kodzie refleksja powinna być wykorzystywana ekstremalnie rzadko. Pisanie o gwarancji jest lekko naciągane, ale skupiłem się na typowym użyciu a nie wyjątkach od reguły.