例如,让我们声明:
private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
get
{
return _strings;
}
}
现在我们可以做到以下几点:
((List<string>) obj.Strings).Add("Hacked");
所以我们真的没有隐藏List
的用法,只是隐藏在界面后面。如何在这样的示例中隐藏列表而不复制新集合中的_strings
来限制列表的修改?
答案 0 :(得分:35)
有很多方法。
最天真的方法是始终复制私有字段:
return _strings.ToList();
但这通常不是必需的。但是, 会有意义,如果:
List<T>
,所以你也可以给一个回复(不暴露原文)。或者,您可以只公开一个只读包装器:
return _strings.AsReadOnly();
这比第一个选项更有效,但会公开对底层集合所做的更改。
这些策略也可以合并。如果您需要原始集合的副本(因此对原始集合的更改没有效果)但也不希望返回的对象是可变的*,您可以使用:
// copy AND wrap
return _strings.ToList().AsReadOnly();
您也可以使用yield
:
foreach (string s in _strings)
{
yield return s;
}
这与第一个选项的效率和权衡相似,但延迟执行。
显然,这里需要考虑的关键是如何实际使用您的方法以及您提供此属性的具体功能。
*虽然说实话,但我不确定你为什么要关心。
答案 1 :(得分:7)
如何使用ReadOnlyCollection
。这应该可以解决这个特殊问题。
private ReadOnlyCollection<string> foo;
public IEnumerable<string> Foo { get { return foo; } }
答案 2 :(得分:5)
我会向你发布关于封装的最新想法,可能你会发现它们合理。
众所周知,封装是非常有用的东西,它有助于对抗复杂性。它使您的代码在逻辑上变得简约和清晰。
正如您所说,可以破坏对成员的受保护访问,并且界面用户可以获得对底层对象的完全访问权限。但问题是什么呢?您的代码不会变得更复杂或耦合。
实际上,据我所知,使用一些反思几乎没有任何限制,即使有完全私人成员。那你为什么不试着隐藏它们呢?
我认为没有必要复制一个集合(或任何其他可变的)并且上面的方法是错误的。使用您的初始变体:
private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
get
{
return _strings;
}
}
答案 3 :(得分:3)
ReadOnlyCollection<T>
:http://msdn.microsoft.com/en-us/library/ms132474.aspx
初始化ReadOnlyCollection类的新实例,该实例是指定列表周围的只读包装。
答案 4 :(得分:3)
你可以将你的列表包装在getter中,如下所示:
private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings {
get {
return new ReadOnlyCollection<string>(_strings);
}
}
通过这种方式,您可以自由修改课堂内的私人列表,而不必担心外面的变化。
注意:ReadOnlyCollection
不会复制其中包含的列表(例如_strings)
答案 5 :(得分:1)
由于没有人提供这个作为答案,这是另一种选择:
private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
get { return _strings.Select(x => x); }
}
它实际上等同于前面提到的yield return
方法,但更紧凑。
答案 6 :(得分:0)
除ReadOnlyCollection
外,还有Immutable Collections for .NET可用。
使用不可变集合向消费者保证集合不会更改而不仅仅是他们无法更改。因为它们是不可变的,你可以公开它们:
public ImmutableList<string> Strings { get; private set; }