从IList<>返回ReadOnlyCollection

14

好的,List<>中包含了AsReadOnly()方法,该方法会返回ReadOnlyCollection。我需要的是一个IList类型的字段和一个能够返回该列表的ReadOnlyCollection的属性。

例如下面的类:

class Test
{
   private IList<Abc> list;

   public AddToList(Abc someItem) { /* adds inside the list */... }

   public ReadOnlyCollection<Abc> List { get { return ??? } } // <- no "set" here!
}
场景是这样的:当项目被添加到列表中时,我需要在我的类内部执行一些自定义逻辑,并且我想通过调用AddToList(someitem)来限制向该列表添加内容,而不允许使用list.Add(someItem)。问题在于我使用了NHibernate,它要求使用IList接口,因此我无法将其强制转换/调用AsReadOnly()方法(它不包含此方法)。
你会推荐什么方法来解决这种情况?我只需要一种方法让NHibernate以某种方式设置所需的集合,但我也需要限制用户。

您可能还想查看http://stackoverflow.com/questions/5522654/extension-methods-for-converting-iqueryablet-and-ienumerablet-to-readonlycol。 - nawfal
5个回答

33

您可以直接模拟 AsReadOnly() 方法:

public ReadOnlyCollection<Abc> List
{
    get { return new ReadOnlyCollection<Abc>(list); }
}

更新:
这并不会创建list的副本。 ReadOnlyCollection 不会复制数据,它直接在提供的列表上操作。请参阅文档:

一个只读集合只是具有包装器的集合,用于阻止修改集合;因此,如果对底层集合进行更改,则只读集合将反映这些更改。
这个构造函数是O(1)操作。


这会通过对所有项目进行迭代来创建一个新集合,还是保留对底层innerCollection的引用? - Denis Biondic
3
它不会创建副本。这段代码与AsReadOnly中使用的完全相同。如果你不想要这种行为,你应该重新表述你的问题。 - Daniel Hilgarth

7
尝试
return new ReadOnlyCollection<Abc> (list);

好的,我也考虑过这个,但我更喜欢不将集合的内容复制到另一个集合的解决方案,而这个会在下面做到这一点,对吗? - Denis Biondic
这将创建一个副本……不复制的解决方案只有在你创建一个实现了IList接口的类并使用它的情况下才可能。 - Yahia
3
@Yahia:错了,它不会创建副本。 - Daniel Hilgarth

1
在我看来,最好的方法是使用Jon Skeet建议的方法返回IEnumerable<Abc>
public IEnumerable<Abc> Abcs {
    get { return list.Skip(0); }
}

如果您不担心消费者将 IEnumerable<T> 转换回 IList<T>,那么只需返回 IEnumerable<T>

ReadOnlyCollection<T> 存在一些严重的缺陷,其中主要问题是它实现了 IList<T>,因此具有添加和删除方法。 如果消费者忽略了其 ReadOnly 属性,则可能导致运行时错误。 我强烈建议不要使用它。


1
请使用以下方式:
public class Test
    {

        private IList<SubTest>      _subTests;


        public virtual void AddSubTest(SubTest SubTest)
        {
            if (_subTests == null)
                _subTests = new List<SubTest>();

            _subTests.Add(SubTest);

            return this;
        }


        public virtual IReadOnlyList<SubTest> SubTests
        {
            get
            {
                if (_subTests == null)
                    _subTests = new List<SubTest>();

                return _subTests.AsReadOnly();
            }
        }




        public void RemoveSubTest( SubTest SubTest )
        {
            if( _subTests == null ) 
                return;

            _subTests.Remove( SubTest );            
        }

    }

使用以下映射,它会将值设置到字段而不是只读列表属性。
<bag 
      name="SubTests" table="SubTest" lazy="true" access="field.camelcase-underscore"
      optimistic-lock="false"
      >
      <key column ="TestID" />
      <many-to-many class="SubTest, Domain" column="SubTestID" />
    </bag>

1

您还可以返回IEnumerable<Abc>,它是只读迭代器,并提供添加和删除元素的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接