我正在使用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。
答案 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)
如果符合以下条件,您可以调用属性设置器:
如果对于其他人定义的接口,您遇到操作的前后条件,则不应遇到问题。
编辑:我同意上述内容。我公开收藏的第一选择是:
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?)。