14

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.

Denis Biondic
  • 7,943
  • 5
  • 48
  • 79
  • You might also want to see http://stackoverflow.com/questions/5522654/extension-methods-for-converting-iqueryablet-and-ienumerablet-to-readonlycol – nawfal Nov 12 '13 at 10:11

5 Answers5

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.

MUG4N
  • 19,377
  • 11
  • 56
  • 83
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • Will this create a new collection with iteration through all items, or it keeps the reference to the underlying innerCollection ? – Denis Biondic Aug 29 '11 at 08:15
  • 3
    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. – Daniel Hilgarth Aug 29 '11 at 08:18
7

try

return new ReadOnlyCollection<Abc> (list);
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • 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 ? – Denis Biondic Aug 29 '11 at 08:12
  • 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... – Yahia Aug 29 '11 at 08:14
  • 3
    @Yahia: Wrong. It doesn't create a copy – Daniel Hilgarth Aug 29 '11 at 08:20
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>
Low Flying Pelican
  • 5,974
  • 1
  • 32
  • 43
1

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

Mohamed Abed
  • 5,025
  • 22
  • 31
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.

Community
  • 1
  • 1
Jamie Ide
  • 48,427
  • 16
  • 81
  • 117