Return ReadOnlyCollection from IList<>
Asked Answered
D

6

13

OK, so List<> contains the AsReadOnly() which gives you the ReadOnlyCollection. What I need is to have a field of IList type, and a property which would return a ReadOnlyCollection for this list.

Example class:

class Test
{
   private IList<Abc> list;

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

   public ReadOnlyCollection<Abc> List { get { return ??? } } // <- no "set" here!
}

The scenario is following: I need to have some custom logic inside my class when the item is added into the list, and I want to restrict adding to this list by calling AddToList(someitem), while not allowing the usage of list.Add(someItem). The problem is that I use NHibernate which requires the IList interface, so I cannot cast / call the AsReadOnly() on the IList (it does not contain this method).

What way would you recommend to solve this situation? I simply need a way for NHibernate to set the needed collection in some way, but I also need to restrict users.

Duax answered 29/8, 2011 at 8:6 Comment(1)
You might also want to see #5523154Cusack
P
33

You can just emulate AsReadOnly():

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

UPDATE:
This doesn't create a copy of list. ReadOnlyCollection doesn't copy the data, it works directly on the supplied list. See documentation:

A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes.
This constructor is an O(1) operation.

Peder answered 29/8, 2011 at 8:10 Comment(2)
Will this create a new collection with iteration through all items, or it keeps the reference to the underlying innerCollection ?Duax
It doesn't create a copy. This is exactly the same code as is used in AsReadOnly. If you don't want that behavior, you should rephrase your question.Peder
S
7

try

return new ReadOnlyCollection<Abc> (list);
Stenopetalous answered 29/8, 2011 at 8:9 Comment(3)
OK, I also thought of this, but I would prefer the solution which doesn't copy over the contents of the collection to another collection, and this will do that beneath, right ?Duax
this will make a copy... a solution without copying is only possible IF you create a class the implements the IList interface and use that...Stenopetalous
@Yahia: Wrong. It doesn't create a copyPeder
U
1

Use following way,

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 );            
        }

    }

use following mapping, and it would sets the value to the field not to the property which is read only list

<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>
Unreel answered 29/8, 2011 at 8:14 Comment(0)
A
1

You can also return IEnumerable<Abc> which is read only iterator and provide methods for adding and removing elements.

Akihito answered 29/8, 2011 at 11:57 Comment(0)
V
1

In my opinion the best way to do this is to return IEnumerable<Abc> using the method suggested by Jon Skeet:

public IEnumerable<Abc> Abcs {
    get { return list.Skip(0); }
}

If you're not worried about consumers casting the IEnumerable<T> back to IList<T> you can just return IEnumerable<T>.

ReadOnlyCollection<T> has some serious flaws, the primary one being that it implements IList<T> so it does have Add and Remove methods. This can lead to runtime errors if consumers neglect to check its ReadOnly property. I strongly recommend against using it.

Vesica answered 29/8, 2011 at 12:43 Comment(0)
C
0

As of .NET 7, there's now an extension method available on the IList interface that you can use:

IList.AsReadOnly()

https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly?view=net-8.0

Calton answered 16/3 at 14:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.