Return ReadOnlyCollection from IList<>
-
15-01-2021 - |
Question
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.
Solution
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.
OTHER TIPS
try
return new ReadOnlyCollection<Abc> (list);
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>
You can also return IEnumerable<Abc>
which is read only iterator and provide methods for adding and removing elements.
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.