24

例如让我们声明:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
    get
    {
        return _strings;
    }
}

现在我们可以执行以下操作:

((List<string>) obj.Strings).Add("Hacked");

所以我们真的没有隐藏List使用,而只是隐藏在界面后面。如何在此类示例中隐藏列表而不复制_strings新集合以限制对列表的修改?

4

7 回答 7

35

方法很多。

最天真的方法是总是复制私有字段:

return _strings.ToList();

但这通常不是必需的。但是,如果

  1. 你碰巧知道调用代码通常会想要一个List<T>,所以你最好还给一个(不暴露原始代码)。
  2. 即使原始集合随后被更改,暴露的对象保持原始值也很重要。

或者,您可以只公开一个只读包装器:

return _strings.AsReadOnly();

这比第一个选项更有效,但会暴露对基础集合所做的更改。

这些策略也可以结合起来。如果您需要原始集合的副本(因此对原始集合的更改无效)但也不希望返回的对象是可变的*,您可以使用:

 // copy AND wrap
return _strings.ToList().AsReadOnly();

你也可以使用yield

foreach (string s in _strings)
{
    yield return s;
}

这在效率和权衡上与第一个选项相似,但延迟执行。

显然,这里要考虑的关键是您的方法实际上将如何被使用,以及您打算提供此属性的确切功能。

* 虽然说实话,我不知道你为什么会在意。

于 2013-05-07T15:21:32.360 回答
7

使用ReadOnlyCollection. 那应该可以解决该特定问题。

private ReadOnlyCollection<string> foo;
public IEnumerable<string> Foo { get { return foo; } }
于 2013-05-07T15:20:50.937 回答
5

我只是将我最近关于封装的想法发布给你,也许你会发现它们是合理的。

众所周知,封装是非常有用的东西,它有助于对抗复杂性。它使您的代码在逻辑上简洁明了。

正如您所说,可以破坏对成员的受保护访问,并且接口用户可以获得对底层对象的完全访问权限。但它有什么问题呢?您的代码不会变得更复杂或耦合。

实际上,据我所知,使用一些反射几乎没有限制,即使是完全私有的成员。那么你为什么不试着把它们也隐藏起来呢?

我认为没有必要复制一个集合(或任何其他可变的)并且上面的方法是错误的。使用您的初始变体:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings
{
    get
    {
        return _strings;
    }
}
于 2013-05-07T16:03:21.787 回答
3

ReadOnlyCollection<T>:http: //msdn.microsoft.com/en-us/library/ms132474.aspx

初始化 ReadOnlyCollection 类的新实例,该实例是指定列表的只读包装器。

于 2013-05-07T15:20:53.610 回答
3

你可以像这样将你的列表包装在 getter 中:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings {
  get {
    return new ReadOnlyCollection<string>(_strings);
  }
}

这样你就可以在你的班级内自由地修改你的私人名单,而不必担心外面的变化。

注意:ReadOnlyCollection不会复制它包含的列表(例如_strings)

于 2013-05-07T15:24:25.870 回答
1

由于没有人提供这个作为答案,这里有另一种选择:

private readonly List<string> _strings = new List<string>();
public IEnumerable<string> Strings 
{
    get { return _strings.Select(x => x); }
}

它实际上等同于yield return前面提到的方法,但更紧凑一些。

于 2013-05-08T01:41:47.813 回答
0

除此之外,ReadOnlyCollection还有真正的 .NET 不可变集合可用。

使用不可变集合向消费者保证集合不会改变,而不仅仅是他们不能改变它们。由于它们是不可变的,您可以公开它们:

public ImmutableList<string> Strings { get; private set; }

更多详细信息:http: //blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx

于 2013-05-07T15:22:56.357 回答