是否隐式调用.Net属性设置器?

时间:2010-01-26 17:05:58

标签: c# .net asp.net session properties

我正在使用C#的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(),然后让未命名对象超出范围。这将是一个错误,因为存储在Session中的值将保持不变。但显然,它足够聪明,可以调用属性setter并将新更改的集合序列化回会话。

这让我有点紧张,因为我们的遗留代码中有些地方属性设置器有副作用,我不希望那些被调用,如果不是这样的话。

在这种情况下,是否总是调用属性setter?还有别的事吗?或者我是否完全误解了这里发生的事情?

[补充说明答案]
事实证明确实存在误解。我知道存储在Session中的对象必须是可序列化的,并且基于此我对集合在内部的行为做了太多假设。我在思考。

存储对象只有一个实例(我的IList)。每次调用getter都会返回对同一实例的引用。所以上面引用的代码就像看起来一样,不需要特殊的魔法。

回答标题问题:不,不会暗中调用setter。

3 个答案:

答案 0 :(得分:4)

是的,你是对的,如果你的setter / getters正在序列化/反序列化对象,这将是一个错误。但这种情况并非如此。而是基于参考传递。

所以基本上发生的是你的例子中的第一行通过get获取项目,并且基于此调用Count。然后第二行出去再次调用get,返回相同的对象,运行clear,然后第三行与第一行相同。

如果您已经写过这样的setter / getter,那么您将会遇到“bug”

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

在这种情况下,将创建一个新对象,并为每个get块调用。但是因为上面的例子只是传递对同一个对象的引用,所以你不会看到这个“bug”。

我说“bug”,因为它不是一个真正的错误,它只是对幕后发生的事情的误解。

我希望这会有所帮助。

答案 1 :(得分:1)

您的代码大致相当于:

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,也要清除该集合。因此调试输出似乎正常。

答案 2 :(得分:-1)

如果符合以下条件,您可以调用属性设置器:

  • 公开可见(其他组件可见)。
  • 它们将setter实现为其他程序集可见的界面的一部分。在某些情况下,例如
  • 它们用于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?)。