Chciałbym rozwinąć konkretną kwestię, którą Eric Lippert poruszył w swojej odpowiedzi i skierować uwagę na konkretną okazję, która w ogóle nie została poruszona przez nikogo innego. Eric powiedział:
[...] przypisanie prawie zawsze pozostawia wartość, która została właśnie przypisana w rejestrze.
Chciałbym powiedzieć, że przypisanie zawsze pozostawi wartość, którą próbowaliśmy przypisać do naszego lewego operandu. Nie tylko „prawie zawsze”. Ale nie wiem, ponieważ nie znalazłem tego problemu skomentowanego w dokumentacji. Teoretycznie może to być bardzo skuteczna zaimplementowana procedura „pozostawienia” i nie ponownej oceny lewego operandu, ale czy jest wydajna?
„Wydajne” tak dla wszystkich przykładów, które zostały do tej pory skonstruowane w odpowiedziach w tym wątku. Ale wydajne w przypadku właściwości i indeksatorów, które używają metod dostępu get i set? Ani trochę. Rozważ ten kod:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Tutaj mamy właściwość, która nie jest nawet opakowaniem dla zmiennej prywatnej. Za każdym razem, gdy zostanie wezwany, zwróci prawdę, gdy ktoś spróbuje ustalić swoją wartość, nie powinien nic zrobić. Zatem ilekroć ta właściwość zostanie oceniona, będzie prawdomówny. Zobaczmy co się stanie:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Zgadnij, co to drukuje? To drukuje Unexpected!!
. Jak się okazuje, faktycznie wywoływany jest set accessor, który nic nie robi. Ale potem metoda get nigdy nie jest w ogóle wywoływana. Przypisanie to po prostu pozostawia false
wartość, którą próbowaliśmy przypisać naszej własności. Ta false
wartość jest obliczana przez instrukcję if.
Skończę na przykładzie z prawdziwego świata , dzięki któremu zbadałem ten problem. Zrobiłem indeksator, który był wygodnym opakowaniem dla kolekcji ( List<string>
), którą moja klasa miała jako zmienną prywatną.
Parametr wysyłany do indeksatora był łańcuchem, który miał być traktowany jako wartość w mojej kolekcji. Metoda dostępu get zwróciłaby po prostu prawdę lub fałsz, gdyby ta wartość istniała na liście, czy nie. Zatem List<T>.Contains
metoda get była kolejnym sposobem wykorzystania metody.
Jeśli metoda akcesora set indeksatora została wywołana z łańcuchem znaków jako argumentem, a prawy operand true
byłby bool , dodałby ten parametr do listy. Ale jeśli ten sam parametr false
zostałby wysłany do metody dostępu, a właściwy operand byłby wartością logiczną , zamiast tego usunąłby element z listy. W ten sposób zestaw akcesorów został użyty jako wygodna alternatywa dla obu List<T>.Add
i List<T>.Remove
.
Wydawało mi się, że mam zgrabne i kompaktowe „API” opakowujące listę moją własną logiką zaimplementowaną jako brama. Z pomocą samego indeksatora mogłem zrobić wiele rzeczy za pomocą kilku zestawów naciśnięć klawiszy. Na przykład, jak mogę spróbować dodać wartość do mojej listy i sprawdzić, czy się tam znajduje? Myślałem, że to jedyna niezbędna linia kodu:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Ale jak pokazał mój wcześniejszy przykład, metoda get, która powinna sprawdzić, czy wartość rzeczywiście znajduje się na liście, nie została nawet wywołana. true
Wartość zawsze pozostawione skutecznie niszczyć cokolwiek logika miałem realizowany w moim akcesor get.