3

I don't know if this is possible, but basically, I want to expose a property of type ReadOnlyCollection<ReadOnlyCollection<MyType>>, while still being able to modify the underlying collection inside the class exposing the property. Further, I want users of my class to be able to hold a reference to the collection returned, so that it updates as I update it internally in the class exposing the property.

For instance, If this were a single collection, I could do something like this:

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

Which would still allow for adding items to the list inside my class by doing _List.Add(). Further, if a client of this class was to do, say:

var collectionRef = myClassInstance.RList;

Then collectionRef would also change as I add elements to the list from inside MyClass.

The problem arises if I want a list of lists:

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

The immediate problem with the above attempt is that AsReadonly() only applies to the outer list. So I'd be trying to return a ReadOnlyCollection<List<MyItem>> which is not the declared type for the property. The only way I can think of to satisfy the declared property type would involve creating a new ReadOnlyCollection in a way similar to:

new ReadOnlyCollection<ReadOnlyCollection<MyItem>>(_List)

or

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

But I'm not sure what the syntax for creating this nested collection would be; Also, I'm not sure if by creating 'new' collections, I won't be able to track changes to the underlying lists by means of a ref to the value returned by the property. That is, if inside MyClass I do _List[1].Add(myTypeInstance) I'm not sure that someone holding a ref to the readonly version will see that new item.

I'm open to other approaches all together honesty. Basically I just need to expose a list of lists of items which is readonly but can be modified inside the class that exposes the property and for clients to be able to see the changes reflected without needing to get the value again from the property accessor.

4

2 回答 2

0

ReadOnlyCollection不能做到这一点,至少不能单独做到这一点。如果您向 中添加新列表_List,则任何用户RList都需要有机会看到ReadOnlyCollection链接到该新列表的新列表。ReadOnlyCollection根本不支持。它只是原始项目的只读视图,原始项目是可修改的。

您可以实现一个行为类似于 的自定义类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更改。

于 2014-07-05T23:10:48.913 回答
-1

用于Private Set修改暴露属性的类中的底层集合

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

使用事件通知其他类集合已被修改

我正在添加代码以防上面的链接过期。:/

例子

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.
于 2014-07-06T13:36:29.470 回答