Składowe klasy, których niezmienności jesteśmy pewni, możemy oznaczyć przynajmniej dwojako:
1: public class ConstantValues
2: {
3: public const int Constant = 666;
4: public static readonly int StaticReadonly = 123;
5: }
Efekt ich wykorzystania jest taki sam – mamy dostęp do przypisanych im wartości spoza klasy, jednak nie możemy ich zmienić. Dokładne znaczenie: const to “wartość stała”, a static readonly to “składowa statyczna tylko do odczytu” Konstrukcje te jednak różnią się kilkoma szczegółami. Poniżej wymieniam parę, które aktualnie przyszły mi do głowy. Być może różnic jest więcej, ale i tak najważniejszą zostawiłem na koniec:
1. Składowa readonly może być zainicjowana przy deklaracji LUB w statycznym konstruktorze, ważne, aby miało to miejsce dokładnie jeden raz (czyli możemy przypisać wartość wyliczoną przez statyczną metodę). Z kolei składowa const musi być zainicjowana przy deklaracji za pomocą literału napisowego, liczby, bądź wartości boolowskiej.
2. Tylko składowe const mogą posłużyć jako argumenty w deklaracji użycia atrybutu, czyli to się skompiluje
1: [SomeAttribute(ConstantValues.Constant)]
2: void SomeMethod() { }
a to już nie:
1: [SomeAttribute(ConstantValues.StaticReadonly)]
2: void SomeMethod() { }
3. Najważniejsze: wartości const są podmieniane w czasie kompilacji, podczas gdy wartości static są pobierane podczas działania programu! Najpierw dowód, a potem uzasadnienie “dlaczego to takie ważne”:
1: int constant;
2: int staticReadonly;
3: void UseConstants()
4: {
5: constant = ConstantValues.Constant;
6: staticReadonly = ConstantValues.StaticReadonly;
7: }
Po skompilowaniu ciało powyższej metody przedstawia się w Reflectorze następująco:
1: private void UseConstants()
2: {
3: this.constant = 0x29a;
4: this.staticReadonly = ConstantValues.StaticReadonly;
5: }
I cóż się okazało? Zamiast ConstantValues.Constant mamy 0x29a, czyli nieświęte 666 w hexach, z pominięciem odwołania do klasy zawierającej tą wartość!
Dobra, “no i co z tego?”. Wyobraźmy sobie sytuację, gdy ten przygłupi prymitywny przykład jest częścią bardziej rozbudowanego systemu. Klasa ConstantValues znajduje się w ConstantAssembly.dll, natomiast wartości z niej są pobierane przez algorytmy w Algorithms.dll. W sytuacji, gdy chcemy zmienić wartość składowej ConstantValues.StaticReadonly wszystko jest ok – podmieniamy na co nam trzeba, kompilujemy ConstantAssembly.dll, wrzucamy nową wersję i wszystko śmiga. Co się jednak dzieje, gdy chcemy zmodyfikować ConstantValues.Constant? Podmiana ConstantAssembly.dll nic nie zmienia! Musimy przekompilować nie tylko ConstantAssembly, ale również wszystkie biblioteki z niej korzystające, ponieważ to właśnie podczas KOMPILACJI pobierane i podstawiane są te wartości! Dlatego też trzeba zdawać sobie sprawę z owej różnicy i świadomie stosować odpowiednie rozwiązania. Metodzie “chybił-trafił” mówimy stanowcze i zdecydowane NIE!
Ciekawostkę jest też to, że wartość pól readonly jest jednak writeable… przy pomocy refleksji.