Установщики свойств .Net когда-либо вызывались неявно? - PullRequest
10 голосов
/ 26 января 2010

Я нахожусь на проекте 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, должны быть сериализуемыми, и на основании этого я сделал слишком много предположений о том, как коллекция ведет себя внутренне. Я задумался.

Существует только один экземпляр хранимого объекта (мой IList). Каждый вызов получателя возвращает ссылку на тот же экземпляр. Таким образом, приведенный выше код работает так же, как кажется, без особой магии.

И чтобы ответить на заглавный вопрос: Нет, сеттеры не вызываются неявно.

Ответы [ 3 ]

4 голосов
/ 26 января 2010

Да, вы правы, это было бы ошибкой, если бы ваши сеттеры / геттеры сериализовали / десериализовали объекты. Но это не так. Вместо этого вы проходите на основе ссылки.

Итак, в основном происходит то, что первая строка в вашем примере получает элемент через get, и Count вызывается на основе этого. Затем вторая строка выходит и снова вызывает get, возвращает тот же объект, выполняет clear, а затем третья строка делает то же, что и первая.

Если бы вы написали свой установщик / получатель что-то вроде этого, у вас будет "ошибка"

protected IList<Stuff> RelevantSessionData
{
    get
    {
        return (IList<Stuff>) JSON.ConvertFromString(Session["relevant_key"]);
    }
    set
    {
        Session["relevant_key"] = JSON.ConvertToString(value);
    }
}

В этом случае новый объект будет создан и для каждого вызова блока get. Но так как ваш пример выше просто передает ссылку на тот же объект, вы не увидите эту «ошибку».

И я говорю «ошибка», так как на самом деле это не ошибка, это просто недопонимание того, что происходит за кулисами.

Надеюсь, это поможет.

1 голос
/ 26 января 2010

Ваш код примерно эквивалентен:

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

Даже если вы звоните только получателю, вы очищаете коллекцию. Таким образом, вывод отладки кажется нормальным.

0 голосов
/ 26 января 2010

Вы можете ожидать вызова установщиков свойств, если:

  • Публично видимы (видимы для других сборок).
  • Они реализуют сеттер как часть интерфейса, видимого для других сборок. В некоторых случаях, таких как
  • Они используются в привязке WPF (но платформа будет следовать правилам о BindingMode ).
  • Они используются в MEF с ImportAttribute .
  • Они используются в некоторых других обязательных рамках (вы поняли).

Вы не должны сталкиваться с проблемами, если для интерфейсов, определенных другими, вы соответствуете предварительным и последующим условиям операции.

Редактировать: Я согласен с вышеизложенным. Мой первый выбор для демонстрации коллекции:

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;
}

Бывают ситуации, когда приведенные выше практики не обязательно являются лучшим ответом, но они являются двумя наиболее распространенными (ИМО?).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...