Я хотел бы подробнее остановиться на конкретном моменте, который Эрик Липперт высказал в своем ответе, и обратить внимание на особый случай, который вообще никем не затронут. Эрик сказал:
[...] присвоение почти всегда оставляет значение, которое только что было присвоено в регистре.
Я хотел бы сказать, что присвоение всегда будет оставлять после себя значение, которое мы пытались присвоить нашему левому операнду. Не просто «почти всегда». Но я не знаю, потому что я не нашел комментария к этой проблеме в документации. Теоретически может быть очень эффективной реализованной процедурой «оставить позади» и не переоценивать левый операнд, но эффективна ли она?
«Эффективно» да для всех примеров, построенных на данный момент в ответах этой ветки. Но эффективно в случае свойств и индексаторов, использующих методы доступа get и set? Не за что. Рассмотрим этот код:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Здесь у нас есть свойство, которое даже не является оболочкой для частной переменной. Всякий раз, когда к нему обращаются, он должен возвращать истину, всякий раз, когда кто-то пытается установить свое значение, он ничего не должен делать. Таким образом, всякий раз, когда это свойство оценивается, он должен быть правдивым. Давай посмотрим что происходит:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Угадайте, что он печатает? Он печатает Unexpected!!
. Как оказалось, действительно вызывается метод доступа set, который ничего не делает. Но после этого метод доступа get вообще никогда не вызывается. Присваивание просто оставляет false
значение, которое мы пытались присвоить нашему свойству. И это false
значение оценивается оператором if.
Я закончу реальным примером, который побудил меня исследовать эту проблему. Я сделал индексатор, который был удобной оболочкой для collection ( List<string>
), который мой класс имел в качестве частной переменной.
Параметр, отправленный индексатору, представлял собой строку, которую следовало рассматривать как значение в моей коллекции. Метод доступа get просто вернет true или false, если это значение существует в списке или нет. Таким образом, метод доступа get был еще одним способом использования List<T>.Contains
метода.
Если метод доступа set индексатора был вызван со строкой в качестве аргумента, а правый операнд был логическим значением true
, он бы добавил этот параметр в список. Но если тот же параметр был отправлен в метод доступа, а правый операнд был логическим значением false
, он вместо этого удалял бы элемент из списка. Таким образом, набор аксессуаров использовался как удобная альтернатива обоим List<T>.Add
и List<T>.Remove
.
Я думал, что у меня есть аккуратный и компактный «API», который оборачивает список моей собственной логикой, реализованной в качестве шлюза. С помощью одного только индексатора я мог многое делать с помощью нескольких нажатий клавиш. Например, как я могу попытаться добавить значение в свой список и убедиться, что оно там есть? Я думал, что это единственная необходимая строка кода:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Но, как показал мой предыдущий пример, метод доступа get, который должен видеть, действительно ли значение находится в списке, даже не был вызван. true
Значение всегда было оставлено позади эффективно уничтожая любую логику я осуществил в моем ПОЛУЧИТЬ аксессор.