如何让KeyedCollection成为只读?

首先让我解释为什么我使用KeyedCollection。 我正在构建一个DLL,我有一个项目列表,我需要添加到一个集合,并让它们按照我放置它们的顺序保留,但我还需要通过它们的索引和键来访问它们(关键是我已定义的对象的属性)。 如果有任何其他更简单的集合,请告诉我。

好了,我需要能够在DLL内部向此集合中添加项目,但是我需要将它作为只读公开提供给DLL的最终用户,因为我不希望它们删除/更改我添加的项目。

我一直在这个网站,其他网站,谷歌搜索,我一直无法找到一种方法来获得某种只读的KeyedCollection。 我最接近的是这个页面( http://www.koders.com/csharp/fid27249B31BFB645825BD9E0AFEA6A2CCDDAF5A382.aspx?s=keyedcollection#L28 ),但我无法让它发挥作用。

更新:

我看了看那些C5课程。 这与你的其他评论一起帮助我更好地理解如何创建我自己的只读类,它似乎工作。 但是,当我尝试将常规版本转换为只读版本时,我遇到了问题。 我得到一个编译时无法转换错误。 这是我创建的代码(第一个小类是我最初的代码):

public class FieldCollection : KeyedCollection { protected override string GetKeyForItem(Field field) { return field.Name; } } public class ReadOnlyFieldCollection : KeyedCollection { protected override string GetKeyForItem(Field field) { return field.Name; } new public void Add(Field field) { throw new ReadOnlyCollectionException("This collection is read-only."); } new public void Clear() { throw new ReadOnlyCollectionException("This collection is read-only."); } new public void Insert(int index, Field field) { throw new ReadOnlyCollectionException("This collection is read-only."); } new public bool Remove(string key) { throw new ReadOnlyCollectionException("This collection is read-only."); } new public bool Remove(Field field) { throw new ReadOnlyCollectionException("This collection is read-only."); } new public bool RemoveAt(int index) { throw new ReadOnlyCollectionException("This collection is read-only."); } } 

如果我定义了这个变量:

 private FieldCollection _fields; 

然后这样做:

 public ReadOnlyFieldCollection Fields; Fields = (ReadOnlyFieldCollection)_fields; 

它无法编译。 他们都是从同一个class级inheritance,我认为他们会“兼容”。 如何将集合转换(或公开)为我刚刚创建的只读类型?

我也不知道任何内置解决方案。 这是我对评论提出的建议的一个例子:

  public class LockedDictionary : Dictionary { public override void Add(string key, string value) { //do nothing or log it somewhere //or simply change it to private } //and so on to Add* and Remove* public override string this[string i] { get { return base[i]; } private set { //... } } } 

您将能够遍历KeyValuePair列表和其他所有内容,但它不可用于编写操作。 请务必根据您的需要进行输入。


编辑为了有一个排序列表,我们可以将基类从Dictionary更改为Hashtable

它看起来像这样:

  public class LockedKeyCollection : System.Collections.Hashtable { public override void Add(object key, object value) { //do nothing or throw exception? } //and so on to Add* and Remove* public override object this[object i] { get { return base[i]; } set { //do nothing or throw exception? } } } 

用法:

  LockedKeyCollection aLockedList = new LockedKeyCollection(); foreach (System.Collections.DictionaryEntry entry in aLockedList) { //entry.Key //entry.Value } 

不幸的是,我们无法更改方法的访问修饰符,但我们可以覆盖它们什么都不做。

编辑修改我的原始答案。 显然,我上午没有足够的caffiene。

您应该查看C5集合库,它提供了集合和字典的只读包装,以及CLR团队忘记的其他内容:

http://www.itu.dk/research/c5/

您还可以通过NuGet获取C5 Collections库, url为http://www.nuget.org/packages/C5

您可以使用另外两种选项,这可能会在将来更好地满足类似需求。 第一个选项是最简单的,需要的代码量最少; 第二个需要更多的脚手架,但最适合与现有的KeyedCollection实现共享只读成员。

选项1:从ReadOnlyCollection派生

在这种方法中,您从ReadOnlyCollection派生,然后添加this[TKey key]索引器。 这类似于 @AndreCalil采用的方法,除了ReadOnlyCollection 已经为每个可写入口点抛出一个NotSupportedException ,所以你在那里被覆盖。

这是Microsoft在内部使用的方法,其System.ServiceModel.Internals程序集( 源代码 )中的ReadOnlyKeyedCollection

 class ReadOnlyKeyedCollection : ReadOnlyCollection { KeyedCollection innerCollection; public ReadOnlyKeyedCollection(KeyedCollection innerCollection) : base(innerCollection) { Fx.Assert(innerCollection != null, "innerCollection should not be null"); this.innerCollection = innerCollection; } public TValue this[TKey key] { get { return this.innerCollection[key]; } } } 

选项2:装饰现有的KeyedCollection

ReadOnlyCollection方法唯一真正的缺点是它不会从KeyedCollection实现inheritance任何逻辑。 如果您有很多自定义逻辑,那么您可以选择在现有实现上使用Decorator模式 。 这看起来像是这样的:

 class MyReadOnlyKeyedCollection : MyKeyedCollection, IReadOnlyCollection { MyKeyedCollection innerCollection; public MyReadOnlyKeyedCollection(MyKeyedCollection innerCollection) : base() { this.innerCollection = innerCollection; } protected override InsertItem(Int32 index, TItem item) { throw new NotSupportedException("This is a read only collection"); } //Repeat for ClearItems(), RemoveItem(), and SetItem() public override void YourWriteMethod() { throw new NotSupportedException("This is a read only collection"); } //Repeat for any other custom writable members protected override IList Items { get { return.innerCollection.ToList(); } } //This should cover all other entry points for read operations public override Object YourReadMethod() { this.innerCollection.YourReadMethod(); } //Repeat for any other custom read-only members } 

然而,这显然要编写更多的代码,并且只有在现有的KeyedCollection接口上有相当数量的自定义代码时才真正有意义; 否则,第一种方法更有意义。 鉴于此,最好通过扩展方法集中该逻辑,或者在C#8.0中使用默认接口方法 。