Я хотел бы остановиться на конкретном моменте, который сделал Эрик Липперт в своем ответе, и обратить особое внимание на тот конкретный случай, который вообще никто не затрагивал.Эрик сказал:
[...] присваивание почти всегда оставляет значение, только что назначенное в регистре.
Я бы хотел сказать, чтоприсваивание всегда оставляет значение, которое мы пытались присвоить нашему левому операнду.Не просто "почти всегда".Но я не знаю, потому что я не нашел эту проблему в документации.Теоретически это может быть очень эффективная реализованная процедура, чтобы «оставить позади» и не переоценивать левый операнд, но эффективна ли она?
«Эффективно» да для всех примеров, построенных до сих пор в ответах этого потока.Но эффективны ли в случае свойств и индексаторов, которые используют методы доступа 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.
Я закончу с примером из реального мира , который заставил меня исследовать эту проблему.Я сделал индексатор, который был удобной оболочкой для коллекции (List<string>
), которую мой класс имел в качестве закрытой переменной.
Параметр, отправленный индексатору, представлял собой строку, которая должна была рассматриваться какзначение в моей коллекции.Метод доступа get просто возвращает true или false, если это значение существует в списке или нет.Таким образом, метод доступа get был другим способом использования метода List<T>.Contains
.
Если метод доступа set индексатора вызывался со строкой в качестве аргумента, а правым операндом был bool true
, он добавил бы этот параметрк списку.Но если тот же параметр был отправлен в метод доступа, а правым операндом был bool false
, он вместо этого удалил бы элемент из списка.Таким образом, метод доступа set использовался как удобная альтернатива List<T>.Add
и List<T>.Remove
.
Я думал, что у меня есть аккуратный и компактный «API», оборачивающий список своей собственной логикой, реализованной как шлюз.С помощью только одного индексатора я мог сделать много вещей с помощью нескольких нажатий клавиш.Например, как я могу попытаться добавить значение в мой список и убедиться, что оно там?Я думал, что это была единственная необходимая строка кода:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Но, как показал мой предыдущий пример, метод доступа get, который должен видеть, действительно ли значение в списке, даже не вызывался.Значение true
всегда оставалось позади, эффективно разрушая любую логику, которую я реализовал в своем методе доступа get.