如何公开另一种类型的私有集合的只读集合

本文关键字:集合 何公开 只读 类型 另一种 | 更新日期: 2023-09-27 18:09:26

这是我的问题:有一个类包含一些类的内部集合(或列表,或数组,或类似的东西),它必须公开项目的公共只读集合,这些项目是内部集合中相对项目的属性(或字段)。例如:

//Inner collection consists of items of this class
class SomeClass
{
  public int _age;
  //This property is needed for exposing
  public string Age { get { return this._age.ToString(); } }
}
//Keeps inner collection and expose outer read-only collection
class AnotherClass
{
  private List<SomeClass> _innerList = new List<SomeClass> ();
  public ReadOnlyCollection<string> Ages 
  {
     get 
     {
       //How to implement what i need?
     }
  }
}

我知道一种简单的方法,通过使用一对内部列表来实现这一点,其中第二个列表保存第一个列表所需属性的值。像这样:

//Inner collection consists of items of this class
class SomeClass
{
  public int _age;
  //This property is needed for exposing
  public string Age { get { return this._age.ToString(); } }
}
//Keeps inner collection and expose outer read-only collection
class AnotherClass
{
  private List<SomeClass> _innerList = new List<SomeClass> ();
  private List<string> _innerAgesList = new List<string> ();
  public ReadOnlyCollection<string> Ages 
  {
     get 
     {
       return this._innerAgesList.AsreadOnly();
     }
  }
}

但是我不喜欢这种开销。也许有一些方法可以实现我想要的公开接口。救救我,求你了!

呼啦!看来最好的解决办法已经找到了。由于Groo的帖子

这个问题找到了几乎普遍的答案。下面是It(我们需要添加两个实体):

public interface IIndexable<T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}
class Indexer <Tsource, Ttarget> : IIndexable<Ttarget>
{
    private IList<Tsource> _source = null;
    private Func<Tsource, Ttarget> _func = null;
    public Indexer(IList<Tsource> list, Func<Tsource, Ttarget> projection)
    {
        this._source = list;
        this._func = projection;
    }
    public Ttarget this[int index] { get { return this._func(this._source[index]); } }
    public int Count { get { return _source.Count; } }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    public IEnumerator<Ttarget> GetEnumerator()
    { foreach (Tsource src in this._source) yield return this._func(src); }     
}
使用它们,我们的实现看起来像这样:
//Inner collection consists of items of this class
class SomeClass
{
  public int _age;
  //This property is needed for exposing
  public string Age { get { return this._age.ToString(); } }
}
//Keeps inner collection and expose outer read-only collection
class AnotherClass
{
  private List<SomeClass> _innerList = new List<SomeClass> ();
  private Indexer<SomeClass, string> _indexer = null;
  public AnotherClass () 
  { this._indexer = new Indexer<SomeClass, string > (this._innerList, s => s.Age); }
  public IIndexable<string> Ages { get { return this._indexer; } }
}

感谢Groo和其他回答的人。

如何公开另一种类型的私有集合的只读集合

如果考虑到ReadOnlyCollection是列表的包装器(即它不创建所有项的副本),那么开销就不是那么重要了。

也就是说,如果你的类是这样的:

class AnotherClass
{
    private ReadOnlyCollection<string> _readonlyList;
    public ReadOnlyCollection<string> ReadonlyList
    {
        get { return _readonlyList; }
    }
    private List<string> _list;
    public List<string> List
    {
        get { return _list; }
    }
    public AnotherClass()
    {
        _list = new List<string>();
        _readonlyList = new ReadOnlyCollection<string>(_list);
    }
}

List属性的任何更改都会反映在ReadOnlyList属性中:

class Program
{
    static void Main(string[] args)
    {
        AnotherClass c = new AnotherClass();
        c.List.Add("aaa");
        Console.WriteLine(c.ReadonlyList[0]); // prints "aaa"
        c.List.Add("bbb");
        Console.WriteLine(c.ReadonlyList[1]); // prints "bbb"
        Console.Read();
    }
}

你可能有线程安全的问题,但是暴露IEnumerable更糟糕。

就我个人而言,我使用自定义IIndexable<T>接口和几个方便的包装器类和扩展方法,我在不可变列表的所有代码中都使用这些方法。它允许随机访问列表元素,并且不公开任何修改方法:

public interface IIndexable<T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Length { get; }
}

它还允许整洁的类似LINQ的扩展方法,如Skip, Take和类似的方法,由于索引能力,与LINQ相比具有更好的性能。

在这种情况下,您可以实现这样的投影:

public class ProjectionIndexable<Tsrc, Ttarget> : IIndexable<Ttarget>
{
    public ProjectionIndexable
         (IIndexable<Tsrc> src, Func<Tsrc, Ttarget> projection)
    {
        _src = src;
        _projection = projection;
    }
    #region IIndexable<Ttarget> Members
    public Ttarget this[int index]
    {
        get { return _projection(_src[index]); }
    }
    public int Length
    {
        get { return _src.Length; }
    }
    #endregion
    #region IEnumerable<Ttarget> Members
    // create your own enumerator here
    #endregion
}

并像这样使用:

class AnotherClass
{
    private IIndexable<string> _readonlyList;
    public IIndexable<string> ReadonlyList
    {
        get { return _readonlyList; }
    }
    private List<SomeClass> _list;
    public List<SomeClass> List
    {
        get { return _list; }
    }
    public AnotherClass()
    {
        _list = new List<SomeClass>();
        _readonlyList = new ProjectionIndexable<SomeClass, string>
             (_list.AsIndexable(), c => c.Age);
    }
}

[编辑]

与此同时,我在CodeProject上发布了一篇描述这样一个集合的文章。我看到您已经自己实现了它,但是您仍然可以检查它并重用您认为合适的部分代码。

为什么不直接返回IEnumerable呢?

如果您有权限访问LINQ(。. NET 3.5),然后使用select()

public IEnumerable<string> Ages{
   get{
      return _innerList.Select(s => s.stringProperty);
   }
}

在这种情况下,我通常只使用IEnumerable -如果集合是只读的,你不需要索引功能,你可以这样做:

public IEnumerable<string> Ages
{
   get
   {
      return this._innerList.Select(someObj => someObj.Age).ToArray();
   }
}