Подтвердить что ты не робот

Являются ли NetNet-сеттеры когда-либо называемыми неявно?

Я нахожусь в проекте ASP.Net 2.0, на С#. У меня есть некоторые данные, которые хранятся в состоянии сеанса. Для удобства использования он завернут в свойство, например:

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

Получение и настройка значения работает точно так, как вы ожидали. Если я хочу очистить значение, я просто установил его равным null, и проблем нет. Однако на другой странице разработчика он вызывает метод Clear() коллекции. Я думал, что это будет ошибкой, но, похоже, это работает, и я не понимаю, почему. Он работает так:

Debug.WriteLine(RelevantSessionData.Count);    //outputs, say, 3
RelevantSessionData.Clear();
Debug.WriteLine(RelevantSessionData.Count);    //outputs 0

Почему это работает? Мое наивное ожидание было бы в том, что средняя линия загружает сериализованное значение из сеанса, десериализуется в объект, вызывает Clear() на этом объекте, а затем позволяет безымянному объекту выпасть из области видимости. Это будет ошибкой, потому что значение, хранящееся в сеансе, останется неизменным. Но, по-видимому, он достаточно умен, чтобы вместо этого вызвать средство настройки свойств и сериализовать вновь измененную коллекцию обратно в сеанс.

Это меня немного нервничает, потому что в нашем устаревшем коде есть места, где у разработчиков свойств есть побочные эффекты, и я не хочу, чтобы их вызывали, если они не были предназначены.

Всегда ли вызывающий атрибут свойства вызывается в такой ситуации? Что-то еще происходит? Или я совершенно неправильно понимаю, что здесь происходит?

[Добавлено для объяснения ответа]
Оказывается, неправильно поняли. Я знал, что объекты, хранящиеся в сеансе, должны быть сериализуемыми, и на основании этого я сделал слишком много предположений о том, как коллекция ведет себя внутренне. Я переусердствовал.

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

Вы можете ожидать, что свойства будут вызываться, если:

  • Публично видимы (видимы для других сборок).
  • Они реализуют сеттер как часть интерфейса, видимого для других сборок. В некоторых случаях, например,
  • Они используются в привязке 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;
}

Есть ситуации, когда вышеупомянутые методы не всегда являются лучшим ответом, но это два наиболее распространенных (IMO?).