我正在编写一个具有集合属性的接口,我想要只读它。我不希望界面的用户能够修改集合。我发现创建只读集合属性的典型建议是将属性的类型设置为IEnumerable,如下所示:
private List<string> _mylist;
public IEnumerable<string> MyList
{
get
{
return this._mylist;
}
}
然而,这并不妨碍用户将IEnumerable转换回List并对其进行修改。
如果我使用Yield
关键字而不是直接返回_mylist
,则会阻止我的界面用户修改集合。我是这么认为的,因为那时我只是逐个返回对象,而不是实际的集合。
private List<string> _mylist;
public IEnumerable<string> MyList
{
get
{
foreach(string str in this._mylist)
{
yield return str;
}
}
}
答案 0 :(得分:5)
这是一种合理的方法,可以防止您的类用户返回到List并访问成员。另一种选择是使用_mylist。AsReadOnly(),它返回ReadOnlyCollection<T>
。
但是,我建议不要担心用户如何通过转换为其他内部类型来“破解”您的API。如果您放置IEnumerable<T>
的公共API,则这是最终用户将(或应该)使用的 - 而不是尝试强制转换为List<T>
。试图阻止用户做一些不合适的事情并破坏你的API就好了,特别是因为你无法阻止所有不恰当的用法。
答案 1 :(得分:4)
是的,它是不可改变的。
但是,您应该使用ReadOnlyCollection<string>
代替。
例如:
MyClassName() { //(Constructor)
MyList = new ReadOnlyCollection<string>(_myList);
}
private List<string> _mylist;
public ReadOnlyCollection<string> MyList { get; private set; }
答案 2 :(得分:3)
是的,这很有效。这不是唯一的方法。这是另一种适用于.NET 3.5的方法:
public IEnumerable<string> MyList
{
get { return _myList.Skip(0); }
}
取自question,其中还显示了其他一些方法。
(使用Google找到)。
答案 3 :(得分:1)
如果使用您的类的代码具有完全信任,则无论如何都可以反映并访问私有字段。这将是相当不礼貌的,但铸造返回值只是稍微好一点。如果用户强制转换你的属性并破坏状态,那么这就是他们自己的错误。
直接将列表实例作为IEnumerable返回的一个好处是LINQ可以优化对ElementAt()或Count()等方法的访问,而不会违反IEnumerable的基本契约(因为任何一个也可以通过枚举来实现)。
答案 4 :(得分:0)
是的,生成的序列将是只读的。它还具有延迟执行的优点,因此在某些客户端代码枚举序列中的项之前,实际上不会对循环进行求值(尽管如果在客户端代码尝试枚举内部集合之前更改内部集合,则会产生意外后果)