暴露ReadOnlyCollection ReadOnlyCollection的最好方法,同时仍然能够在类中修改它

时间:2014-07-05 22:20:29

标签: c# readonly-collection

我不知道这是否可行,但基本上,我希望公开类型为ReadOnlyCollection<ReadOnlyCollection<MyType>>的属性,同时仍然能够修改暴露属性的类中的底层集合。此外,我希望我的类的用户能够保存对返回的集合的引用,以便在我在暴露属性的类内部更新它时更新它。

例如,如果这是一个集合,我可以这样做:

  public class MyClass {
    private List<MyType> _List;
    public ReadOnlyCollection<MyType> RList {
      get {
        return _List.AsReadOnly();
      }
    }
  }

通过执行_List.Add(),仍然可以将项目添加到班级列表中。此外,如果这个班级的客户要做,请说:

var collectionRef = myClassInstance.RList;

当我从collectionRef内部向列表中添加元素时,MyClass也会发生变化。

如果我想要一个列表列表,就会出现问题:

  private List<List<MyType>> _List;
    public ReadOnlyCollection<ReadOnlyCollection<MyType>> RList {
      get {
        return _List.AsReadOnly();
      }
    }

上述尝试的直接问题是AsReadonly()仅适用于外部列表。所以我会尝试返回一个ReadOnlyCollection<List<MyItem>>,它不是属性的声明类型。我能想到满足声明的属性类型的唯一方法是以类似于:

的方式创建一个新的ReadOnlyCollection
new ReadOnlyCollection<ReadOnlyCollection<MyItem>>(_List)

new ReadOnlyCollection<ReadOnlyCollection<MyItem>>() {
new ReadOnlyCollection<MyItem>(_List[0]),
new ReadOnlyCollection<MyItem>(_List[1]),
...
}

但是我不确定创建这个嵌套集合的语法是什么;此外,我不确定是否通过创建“新”集合,我将无法通过引用属性返回的值来跟踪对基础列表的更改。也就是说,如果我在MyClass_List[1].Add(myTypeInstance),我不确定持有只读版本的人会看到那个新项目。

我对所有其他方法持开放态度。基本上我只需要公开一个项目列表的列表,这些列表是只读的,但可以在公开属性的类中进行修改,并且客户端能够看到所反映的更改,而无需再从属性访问器中获取值。 / p>

2 个答案:

答案 0 :(得分:0)

ReadOnlyCollection无法做到这一点,至少不是这样。如果您向_List添加新列表,则RList的任何用户都需要有机会看到与该新列表相关联的新ReadOnlyCollectionReadOnlyCollection根本不支持。它只是 原始项目的只读视图,原始项目是可修改的。

您可以实现一个与ReadOnlyCollection类似的自定义类,除了它不会返回原始项目,但包装它们。 ReadOnlyCollection的实现很简单:几乎所有ReadOnlyCollection方法都可以在一行中实现:它们要么抛出无条件异常(例如IList.Add ),返回一个常量值(例如IList.IsReadOnly),或转发到代理容器(例如Count)。

要使其适应您的使用需要进行的更改涉及直接处理项目的各种方法。请注意,这不仅仅是this[int]索引器:您还需要确保相同列表的两个代理比较相等,并提供私有/内部方法以从代理获取原始列表,以使Contains之类的方法。您还需要创建自定义枚举器类型,以确保GetEnumerator不会开始返回原始项目。您需要创建自定义CopyTo实现。但即使记住这些事情,也应该很容易做到。


也就是说,或许有一种不那么干净但更简单的方法:如果你创建一个源自MyReadOnlyCollection<T>的自定义类ReadOnlyCollection<T>,你可以提供一个内部Items成员(转发致ReadOnlyCollection<T>的受保护Items成员。

然后,您可以创建MyReadOnlyCollection<MyReadOnlyCollection<MyClass>>,并从您自己的程序集中调用RList[0].Items.Add,而不必担心外部用户也可以调用它。


如上所述,如果外部列表实际上永远不会改变,那么它可以更简单:在这种情况下,您可以简单地执行类似

的操作
public ReadOnlyCollection<ReadOnlyCollection<MyType>> RList {
  get {
    return _List.ConvertAll(list => list.AsReadOnly()).AsReadOnly();
  }
}

无需监控_List的更改。

答案 1 :(得分:-1)

使用Private Set修改公开属性

的类中的基础集合
private List<MyType> _List;
public ReadOnlyCollection<MyType> RList {
  get {
    return _List.AsReadOnly();
  }
  private set
   {
      _List = value;
   }
}

使用Events通知其他类已修改集合

我在链接过期时添加代码。 :/

示例

The following simple example shows a class, ListWithChangedEvent, which is similar to the standard ArrayList class, but also invokes a Changed event whenever the contents of the list change. Such a general-purpose class could be used in numerous ways in a large 
program.

// events1.cs
using System;
namespace MyCollections 
{
   using System.Collections;

   // A delegate type for hooking up change notifications.
   public delegate void ChangedEventHandler(object sender, EventArgs e);

   // A class that works just like ArrayList, but sends event
   // notifications whenever the list changes.
   public class ListWithChangedEvent: ArrayList 
   {
      // An event that clients can use to be notified whenever the
      // elements of the list change.
      public event ChangedEventHandler Changed;

      // Invoke the Changed event; called whenever list changes
      protected virtual void OnChanged(EventArgs e) 
      {
         if (Changed != null)
            Changed(this, e);
      }

      // Override some of the methods that can change the list;
      // invoke event after each
      public override int Add(object value) 
      {
         int i = base.Add(value);
         OnChanged(EventArgs.Empty);
         return i;
      }

      public override void Clear() 
      {
         base.Clear();
         OnChanged(EventArgs.Empty);
      }

      public override object this[int index] 
      {
         set 
         {
            base[index] = value;
            OnChanged(EventArgs.Empty);
         }
      }
   }
}

namespace TestEvents 
{
   using MyCollections;

   class EventListener 
   {
      private ListWithChangedEvent List;

      public EventListener(ListWithChangedEvent list) 
      {
         List = list;
         // Add "ListChanged" to the Changed event on "List".
         List.Changed += new ChangedEventHandler(ListChanged);
      }

      // This will be called whenever the list changes.
      private void ListChanged(object sender, EventArgs e) 
      {
         Console.WriteLine("This is called when the event fires.");
      }

      public void Detach() 
      {
         // Detach the event and delete the list
         List.Changed -= new ChangedEventHandler(ListChanged);
         List = null;
      }
   }

   class Test 
   {
      // Test the ListWithChangedEvent class.
      public static void Main() 
      {
      // Create a new list.
      ListWithChangedEvent list = new ListWithChangedEvent();

      // Create a class that listens to the list's change event.
      EventListener listener = new EventListener(list);

      // Add and remove items from the list.
      list.Add("item 1");
      list.Clear();
      listener.Detach();
      }
   }
}

<强>输出

This is called when the event fires.
This is called when the event fires.