这是我的问题:有一个类包含一些类的内部集合(或列表,或数组,或类似的东西),它必须公开一个公共的只读项集合,它们是属性(或字段)内部集合中的相关项目.例如:
//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(); } } }
但我不喜欢这个开销.可能有一些方法可以通过暴露接口来做我想做的事情.请帮帮我!
这个问题几乎是普遍的答案.这是它(我们需要添加两个实体):
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; } }
它还允许类似LINT的扩展方法,如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); } }
[编辑]
与此同时,我posted an article在CodeProject上描述了这样一个集合.我已经看到你已经自己实现了它,但你可以查看它并重用你认为合适的部分代码.