"const" vs "static readonly"

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!


Ostatnia uwaga, mająca poniekąd związek z poruszonym tematem: typy wyliczeniowe ("enumy") są traktowane tak samo jak const - ich wartości podstawia kompilator!

Opublikowane 06 czerwca 08 09:20 przez Procent
Filed under: ,

Komentarze:

# sebcyg said on czerwca 10, 2008 00:06:

Ciekawostkę jest też to, że wartość pól readonly jest jednak writeable... przy pomocy refleksji.

Komentarze anonimowe wyłączone

About Procent