Я работаю над проектом ASP.Net 2.0 в C #. У меня есть некоторые данные, которые хранятся в состоянии сеанса. Для простоты использования оно заключено в свойство, например:
protected IList<Stuff> RelevantSessionData
{
get
{
return (IList<Stuff>) Session["relevant_key"];
}
set
{
Session["relevant_key"] = value;
}
}
Получение и установка значения работает точно так, как вы ожидаете. Если я хочу очистить значение, я просто устанавливаю его на ноль, и нет никаких проблем. Однако на странице другого разработчика он вызывает метод Clear () коллекции. Я думал, что это будет ошибка, но, кажется, работает, и я не понимаю, почему. Это работает так:
Debug.WriteLine(RelevantSessionData.Count); //outputs, say, 3
RelevantSessionData.Clear();
Debug.WriteLine(RelevantSessionData.Count); //outputs 0
Почему это работает? Мое наивное ожидание состояло бы в том, что средняя строка загружает сериализованное значение из сеанса, десериализуется в объект, вызывает Clear()
для этого объекта, а затем позволяет безымянному объекту выпасть из области видимости. Это было бы ошибкой, потому что значение, сохраненное в Session, останется неизменным. Но, очевидно, он достаточно умен, чтобы вместо этого вызывать установщик свойств и сериализовать вновь измененную коллекцию обратно в сеанс.
Это заставляет меня немного нервничать, потому что в нашем унаследованном коде есть места, где у установщиков свойств есть побочные эффекты, и я не хочу, чтобы те вызывались, если он не предназначен.
Всегда ли вызывается установщик свойств в такой ситуации? Что-то еще происходит? Или я полностью неправильно понимаю, что здесь происходит?
[Добавлено для объяснения ответа]
Оказывается, неправильно поняли. Я знал, что объекты, хранящиеся в Session, должны быть сериализуемыми, и на основании этого я сделал слишком много предположений о том, как коллекция ведет себя внутренне. Я задумался.
Существует только один экземпляр хранимого объекта (my IList
). Каждый вызов получателя возвращает ссылку на тот же экземпляр. Таким образом, приведенный выше код работает так же, как и кажется, без особой магии.
И чтобы ответить на заглавный вопрос: Нет, сеттеры не называются неявно.
Да, вы правы, это было бы ошибкой, если ваши сеттера / Getters были сериализация / десериализация объекты. Но это не так. Вместо этого вы проходите на основе ссылки.
Так что в основном происходит, это то, что первая строка в вашем примере получает элемент через GET, а количество называется на основе этого. Затем линия Seccond выходит и вызывает снова, возвращая один и тот же объект, прозрачный, а затем третья строка делает то же самое, что и первая.
Если вы написали свой сеттер / Getter что-то подобное, у вас будет «ошибка»
protected IList<Stuff> RelevantSessionData
{
get
{
return (IList<Stuff>) JSON.ConvertFromString(Session["relevant_key"]);
}
set
{
Session["relevant_key"] = JSON.ConvertToString(value);
}
}
в этом случае, будет создан новый объект, и для каждого вызова на блок Get. Но поскольку ваш пример выше просто передается по ссылке на тот же объект, вы не увидите эту «ошибку».
И я говорю «BUG», так как это не совсем ошибка, это просто более недопонимание того, что происходит за кулисами.
Я надеюсь, что это поможет.
Вы можете ожидать вызывающих сопутников, если:
Вы не должны столкнуться с проблемами, если для интерфейсов, определенных другими, вы соответствуете предварительному и постусловию операции.
Редактировать: Я согласен с вышеупомянутым. Мой первый выбор для разоблачения коллекции:
private readonly List<T> _sources = new List<T>();
/* Or ICollection<T>, ReadOnlyCollection<T>, or IList<T>, or
* (only a real option for `internal` types) List<T>
*/
public IEnumerable<T> Sources
{
get
{
return _sources;
}
}
Если вы абсолютно должен инициализировать список после создания объекта, то вы можете использовать что-то вроде этого как второе вариант:
public IList<T> Sources
{
get;
private set;
}
Есть ситуации, где Вышеуказанные практики не обязательно являются лучшим ответом, но это два наиболее распространенных (IMO?).
Ваш код примерно эквивалентен:
Debug.WriteLine(((IList<Stuff>) Session["relevant_key"]).Count); //outputs, say, 3
((IList<Stuff>) Session["relevant_key"]).Clear();
Debug.WriteLine(((IList<Stuff>) Session["relevant_key"]).Count); //outputs 0
Даже если вы звоните только на Getter, вы очищаете коллекцию. Таким образом, вывод отладки кажется нормальным.