代码之家  ›  专栏  ›  技术社区  ›  izb

如何允许对私有集合进行迭代而不进行修改?

  •  6
  • izb  · 技术社区  · 15 年前

    如果我有下列班级成员:

    private List<object> obs;
    

    我想允许遍历这个列表作为类接口的一部分,我该怎么做?

    公开是行不通的,因为我不想直接修改列表。

    8 回复  |  直到 13 年前
        1
  •  12
  •   casperOne    14 年前

    你会把它作为 IEnumerable<T> ,但不只是直接返回:

    public IEnumerable<object> Objects { get { return obs.Select(o => o); } }
    

    既然你说你只想要 遍历 在名单中,这是你所需要的。

    你可能会想把 List<object> 直接作为 IEnumerable<t> ,但这是不正确的,因为可以很容易地检查 IEnumerable<t> 在运行时,确定它是 List<T> 把它投到某物上,使其内容发生变异。

    但是,通过使用 return obs.Select(o => o); 最后返回一个迭代器 列表<对象> ,不是直接引用 列表<对象> 本身。

    有些人可能认为,根据C语言规范第7.15.2.5节,这符合“退化表达式”的条件。然而, Eric Lippert goes into detail as to why this projection isn't optimized away

    而且,人们建议人们使用 AsEnumerable extension method . 这是不正确的,因为保留了原始列表的引用标识。从文件的备注部分:

    这个 AsEnumerable<TSource>(IEnumerable<TSource>) 方法除了从实现 IEnumerable<t> IEnumerable<t> 本身。

    换句话说,它所做的只是将源参数强制转换为 IEnumerable<t> ,这无助于保护引用的完整性,将返回原始引用并将其强制转换回 列表<t> 用来改变列表。

        2
  •  11
  •   bruno conde    15 年前

    你可以用 ReadOnlyCollection 或复制 List 并返回它(考虑到复制操作的性能损失)。你也可以使用 List<T>.AsReadOnly

        3
  •  3
  •   Pete    15 年前

    这已经说过了,但我不认为任何答案是超级清楚的。

    最简单的方法是简单地返回readonlycollection

    private List<object> objs;
    
    public ReadOnlyCollection<object> Objs {
        get {
            return objs.AsReadOnly();
        }
    }
    

    这样做的缺点是,如果以后要更改实现,则某些调用方可能已经依赖于这样一个事实,即集合提供随机访问。所以更安全的定义是公开一个IEnumerable

    public IEnumerable<object> Objs { 
        get {
            return objs.AsReadOnly();
        }
    }
    

    请注意,不必调用asreadonly()来编译此代码。但如果你不这样做,调用者我只是把返回值转换回一个列表并修改你的列表。

    // Bad caller code
    var objs = YourClass.Objs;
    var list = objs as List<object>;
    list.Add(new object); // They have just modified your list.
    

    同样的潜在问题也存在于这个解决方案中

    public IEnumerable<object> Objs { 
        get { 
            return objs.AsEnumerable(); 
        } 
    }
    

    所以我建议您在列表中调用asreadonly()并返回该值。

        4
  •  2
  •   Brian Leahy    15 年前

    在接口中添加以下方法签名: 公共IEnumerable遍历列表()

    按此类推:

    public IEnumerable<object> TraverseTheList()
    {
    
        foreach( object item in obj)
        {
          yield return item;
        }
    
    
    }
    

    这将允许您执行以下操作:

    foreach(object item in Something.TraverseTheList())
    {
    // do something to the item
    }
    

    yield返回告诉编译器为您构建一个枚举器。

        5
  •  2
  •   dplante Tschallacka    13 年前

    您可以通过两种方式执行此操作:

    1. 将列表转换为只读集合:

      new System.Collections.ObjectModel.ReadOnlyCollection<object>(this.obs)
      
    2. 或者返回一个IEnumerable项:

      this.obs.AsEnumerable()
      
        6
  •  1
  •   Mitch Wheat    15 年前

    揭露一 ReadOnlyCollection<T>

        7
  •  1
  •   brian    15 年前

    关于这个问题的有趣的帖子和对话: http://davybrion.com/blog/2009/10/stop-exposing-collections-already/

        8
  •  0
  •   BlueMonkMN    15 年前

    您是否考虑过从System.Collections.ReadOnlyCollectionBase派生类?

    推荐文章