首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我怎样才能让KeyedCollection成为只读的?

我怎样才能让KeyedCollection成为只读的?
EN

Stack Overflow用户
提问于 2012-08-10 20:07:11
回答 4查看 1.8K关注 0票数 3

首先,让我解释一下为什么我要使用KeyedCollection。我正在构建一个DLL,我有一个需要添加到集合中的项列表,并让它们按照我放置的顺序保持不变,但我也需要通过索引和键来访问它们(键是我已经定义的对象的属性)。如果还有其他更简单的收藏可以做到这一点,那么请告诉我。

现在,我需要能够在DLL内部将项添加到集合中,但我需要它对DLL的最终用户公开可用,因为我不希望它们删除/更改我添加的项。

我已经搜索了这个网站,其他网站,谷歌在一般情况下,我还没有找到一个方法来获得某种形式的只读KeyedCollection。我最近的一次访问是这个页面(http://www.koders.com/csharp/fid27249B31BFB645825BD9E0AFEA6A2CCDDAF5A382.aspx?s=keyedcollection#L28),但我无法完全完成它的工作。

更新:

我看了一下那些C5类。这一点,连同你的其他评论,帮助我更好地理解如何创建自己的只读类,这似乎是可行的。然而,当我试图将常规的转换为只读的时候,我遇到了一个问题。我得到一个编译时不能转换错误。下面是我创建的代码(第一个小类是我最初拥有的):

代码语言:javascript
复制
public class FieldCollection : KeyedCollection<string, Field>
{
    protected override string GetKeyForItem(Field field)
    {
        return field.Name;
    }
}

public class ReadOnlyFieldCollection : KeyedCollection<string, Field>
{
    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."); }
}

如果我定义了这个变量:

代码语言:javascript
复制
private FieldCollection _fields;

然后这样做:

代码语言:javascript
复制
public ReadOnlyFieldCollection Fields;
Fields = (ReadOnlyFieldCollection)_fields;

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-08-10 20:20:03

我也不知道有什么内置的解决方案。这是我在评论中提出的建议的一个例子:

代码语言:javascript
复制
    public class LockedDictionary : Dictionary<string, string>
    {
        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<T,T>更改为Hashtable

看起来是这样的:

代码语言:javascript
复制
    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?
            }
        }
    }

用法:

代码语言:javascript
复制
        LockedKeyCollection aLockedList = new LockedKeyCollection();

        foreach (System.Collections.DictionaryEntry entry in aLockedList)
        {
            //entry.Key
            //entry.Value
        }

不幸的是,我们不能更改方法的访问修饰符,但是我们可以重写它们而不做任何事情。

票数 2
EN

Stack Overflow用户

发布于 2012-08-13 17:09:06

修改了我原来的答案。很明显,我还没喝够这杯咖啡

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

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

您还可以在C5上通过NuGet获得http://www.nuget.org/packages/C5集合库。

票数 2
EN

Stack Overflow用户

发布于 2017-10-05 00:07:09

有两个额外的选项可供您使用,这可能更适合类似的需求在未来。第一个选项是最简单的,需要最少的代码;第二个选项需要更多的脚手架,但是对于与现有的KeyedCollection<TKey, TValue>实现共享只读成员来说是最优的。

选项1:从ReadOnlyCollection<TValue>派生

在这种方法中,您从ReadOnlyCollection派生,然后添加this[TKey key]索引器。这类似于上面@AndreCalil所采用的方法,只不过ReadOnlyCollection已经为每个可写入口点抛出了一个NotSupportedException,所以我们将在这里进行介绍。

这是微软在ReadOnlyKeyedCollection<TKey, TValue>程序集中( System.ServiceModel.Internals程序集(来源))中与其内部使用的方法:

代码语言:javascript
复制
class ReadOnlyKeyedCollection<TKey, TValue> : ReadOnlyCollection<TValue> {

  KeyedCollection<TKey, TValue> innerCollection;

  public ReadOnlyKeyedCollection(KeyedCollection<TKey, TValue> 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<TKey, TValue>

ReadOnlyCollection<TValue>方法唯一真正的缺点是它不会继承KeyedCollection<TKey, TValue>实现中的任何逻辑。如果您有很多自定义逻辑,那么您可以选择在现有实现上使用装饰图案。这看起来应该是:

代码语言:javascript
复制
class MyReadOnlyKeyedCollection : MyKeyedCollection, IReadOnlyCollection<TValue> {

  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<T> 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<TKey, TItem>接口上有相当数量的自定义代码时才有意义;否则,第一种方法就更有意义了。考虑到这一点,最好通过可拓方法或者在C# 8.0中通过默认接口方法来集中这个逻辑。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11908527

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档