C# .Net属性设置程序是否曾被隐式调用?

C# .Net属性设置程序是否曾被隐式调用?,c#,.net,asp.net,session,properties,C#,.net,Asp.net,Session,Properties,我在做一个ASP.NET2.0项目,在C#中。我有一些数据存储在会话状态中。为便于使用,将其包装在属性中,如下所示: protected IList<Stuff> RelevantSessionData { get { return (IList<Stuff>) Session["relevant_key"]; } set { Session["relevant_key"] = value;

我在做一个ASP.NET2.0项目,在C#中。我有一些数据存储在会话状态中。为便于使用,将其包装在属性中,如下所示:

protected IList<Stuff> RelevantSessionData
{
    get
    {
        return (IList<Stuff>) Session["relevant_key"];
    }
    set
    {
        Session["relevant_key"] = value;
    }
}
为什么这样做有效?我天真的期望是,中间行从会话加载序列化的值,反序列化为一个对象,对该对象调用
Clear()
,然后让未命名的对象脱离范围。这将是一个bug,因为会话中存储的值将保持不变。但显然,它足够聪明,可以调用属性setter并将新更改的集合序列化回会话

这让我有点紧张,因为在我们的遗留代码中,有些地方属性设置程序会产生副作用,我不希望在无意中调用它们

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

[添加以解释答案]
结果确实是误解了。我知道会话中存储的对象必须是可序列化的,基于此,我对集合的内部行为做了太多假设。我想得太多了

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


回答标题问题:不,setter不是隐式调用的。

是的,您是对的,如果setter/getter正在序列化/反序列化对象,这将是一个bug。但事实并非如此。而是基于引用传递

因此,基本上发生的是,示例中的第一行通过get获取项目,并基于此调用Count。然后第二行将退出并再次调用get,返回相同的对象,运行清除,然后第三行将执行与第一行相同的操作

如果你写了这样的setter/getter,你会有一个“bug”

受保护的IList相关会话数据
{
得到
{
return(IList)JSON.ConvertFromString(Session[“related_key]”);
}
设置
{
Session[“related_key”]=JSON.ConvertToString(值);
}
}
在这种情况下,将创建一个新对象,并为每次调用get块创建一个新对象。但是,因为上面的示例只是传递对同一对象的引用,所以您不会看到这个“bug”

我说“虫子”,因为它不是真正的虫子,它更多的是对幕后发生的事情的误解


我希望这会有所帮助。

您的代码大致相当于:

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
Debug.WriteLine(((IList)会话[“相关_键]).Count)//例如,输出3
((IList)会话[“相关_键]).Clear();
Debug.WriteLine(((IList)会话[“相关_键]).Count)//产出0

即使您只调用getter,您也正在清除集合。因此,调试输出似乎正常。

您可以期望在以下情况下调用属性设置程序:

  • 这些程序集是公共可见的(对其他程序集可见)
  • 它们将setter实现为对其他程序集可见的接口的一部分。在某些情况下,例如
  • 它们在WPF绑定中使用(但框架将遵循有关的规则)
  • 它们在MEF中与一起使用
  • 它们在其他绑定框架中使用(您可以理解)
对于其他人定义的接口,如果满足操作的前置和后置条件,则不应该遇到问题

编辑:我同意上述观点。我公开收藏的第一个选择是:

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;
    }
}
private readonly List_sources=new List();
/*或ICollection、ReadOnlyCollection或IList,或
*(仅限“内部”类型的实物期权)列表
*/
公共数字源
{
得到
{
返回源;
}
}
如果绝对必须在创建对象后初始化列表,则可以使用以下内容作为第二个选项:

public IList<T> Sources
{
    get;
    private set;
}
公共IList源
{
得到;
私人设置;
}

在某些情况下,上述做法不一定是最佳答案,但它们是最常见的两种(IMO?)。

+1。事实上,集合属性的最佳实践是使它们成为只读的(根本没有setter)。我想你没有领会我的意思。我知道属性访问器的作用,但我的错误是假设HttpSessionState在每次访问时都序列化/反序列化。我错过了序列化/反序列化部分。我应该更仔细地阅读这个问题。啊哈!我曾假设,因为会话中的值必须是可序列化的,所以它们将以序列化状态存储,并且每个get将返回不同的引用。如果不是这样的话,而且在多个电话中返回同一个参考是明智的,那么我只是想得太多了。-1,答案完全偏离了原始帖子的主题。OP询问为什么给定的代码可以工作,以及他是否可以预期由于重新创建对象而出现错误。
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;
}