Return ReadOnlyCollection from IList<> - c#

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.

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.

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.

Related

Implement API with calls that return an item from a list, or the first item in the list

I have a large library of code that assumes fields contain a single value of class T. Unfortunately, a new client requires us to have those fields point to List(Of T). I can change the field type, but then all the older code breaks.
In VB I would solve this this way:
Private theList As List(Of T) = new List(Of T)
Public ReadOnly Property Thing() As T
Get
Return theList(0)
End Get
End Property
Public ReadOnly Property Thing(i As Integer) As T
Get
Return theList(i) 'yes, this should throw
End Get
End Property
This relies on VB's ability to have multiple properties with the same name, and the overloading the parenthesis for both parameter passing and indexing. However, C# uses braces for the later, and does not have anything corresponding to a parameterized property. Instead, they have "this[]", and since that name is private you cannot have a "this" with no parameter and another with one (that is true, right?).
But how would I do this in C#? I suspect I can do this with a template, but I'm a bit lost how it would look. I can imagine a List(Of T) subclass with a this[], but then I'm not sure how I would implement the accessors in the other classes so that I still have Thing and Thing[i]. And one caveat, these objects are often used from VBA so it needs to be COM-exportable.
You could add a method to access the property instead if the getter itself.
For example:
class Eg
{
List<T> Test { get; set; }
T GetTest()
{
return Test[0];
}
T GetTest(int index)
{
return Test[index];
}
}
You would then access the property like so (and if wanted you could remove the getter):
Eg eg = new Eg();
T t = eg.GetTest();
or
T t = eg.GetTest(i);
I would Suggest using FirstOrDefault from the Linq library, as List[0] will error on an empty list andd also means that you can't switch to any other datatype that doesn't support index reading
private List<T> items = new List<T>();
public T Item
{
get { return items.FirstOrDefault(); }
}
public List<T> Items
{
get { return items; }
}
however properties don't accept parametrisation except in the form of a this Property
if you used a method for this then that will work eg
public T GetData()
{
get { return items.FirstOrDrfault(); }
}
public T GetData(int index)
{
get { return items[index]; }
}
If you need to make your class provide functionality to a new client, you might need to change your design to have them depend on an Interface, rather than the specific implementation of the class.
That said, if sounds like your new client needs a different class, which might contain a collection of your old class, rather than changing your class to fit the new client.

Casting contents of an indexer's returned collection?

I have a table/row/column data structure setup. There is a string-based indexer in the DtaTable class to return DtaRows, and another on the DtaRow class to return DtaColumns. So you can write things like...
return theTables["tablename"]["rowidentifier"]["columnname"];
In actuality, the objects inside the tables are not DtaRows, but one of about three dozen subclasses, like InflationRow and CurrencyRow. Each table contains only objects of those types, so for instance...
theTables["Inflations"]["General"];
always returns an InflationRow.
Now to make this easier to access from C#, I have a bunch of methods at a higher level like...
public DtaTable Inflations { get {return pTables["Inflations"];} }
Now the problem I'd like to solve is that when someone calls one of these methods, they don't get an InflationRow, because DtaTable has DtaRows. So for instance...
MyInfRow = Inflations["General"];
returns a DtaRow. So I have to cast all the time...
MyInfRow = (InflationRow)Inflations["General"];
I want to get rid of all the casting.
The only solution I have found so far is to make 36 new subclasses of the table object, each overriding the indexer return type. This seems worse than the casting.
Is there some simpler way to do this?
It you know that callers are only primarily going to use another indexer, you can introduce a generic class providing that:
public class SpecializedTable<T>
{
private readonly DtaTable table;
// Just in case anyone really wants this
public DtaTable Table { get; }
public SpecializedTable(DtaTable table)
{
this.table = table;
}
public T this[string row] { get { return (T) (object) table[row]; } }
}
As an aside, these DtaTable etc names feel annoying unpronounceable / easily confusable with the .NET DataTable classes. If you're in a position to rename them, I'd suggest you do so.
Then your Inflations property can be:
public SpecializedTable<InflationRow> Inflations
{
get
{
return new SpecializedTable<InflationRow>(pTables["Inflations"]);
}
}
You may want to cache this to avoid creating a new object each time you call the property though.
At that point, this code: Inflations["General"] will perform the cast appropriately for you.
Use as instead of direct cast. If casting is valid it will return the instance, otherwise it will stay as NULL.
public MyInfRow Inflations { get {return pTables["Inflations"] as MyInfRow } }

Exposing interfaces instead of concrete classes

I've recently read something about using interfaces when exposing collections instead of concrete implementations (IEnumerable instead of List). I'm trying to do that now in my code. However, when I expose a property that return IEnumerable, I'm having some difficulty of not allowing nulls as a return value. Example:
public class HumanResource
{
public IEnumerable<EmployeeModel> Employees
{
get
{
// return what?
}
}
}
What should I return in the getter? I don't want to use automatic properties for this as I want to avoid nulls. What I want is to return a new collection with no items. Of course I can return any type that implements IEnumerable but how will the external user of the class know that? Or did I understand this exposing interface instead of concrete implementations wrong?
EDIT: Removed setter
Of course I can return any type that implements IEnumerable but how will the external user of the class know that?
They don't have to know that, that's exactly the point.
Your property promises to return an IEnumerable<EmplyeeModel>, and that's exactly what happens. It doesn't matter which class implementing this interface your code returns.
What I want is to return a new collection with no items.
So, Enumerable.Empty<EmplyeeModel>() or new List<EmployeeModel>() will do just fine.
When designing an API you need to think about what the consumers will do with the data types you return, and decide upon that accordingly.
Usually an IEnumerable<T> for collections suits everyone. When they want it in a list, they can do new List<T>(yourEnumerable), or yourEnumerable.ToArray() to use it as an array.
What I want is to return a new collection with no items.
Properties let you do that very easily:
public class HumanResource
{
// This is the real employees that gets returned when its not null
private IEnumerable<EmployeeModel> employees; // may be null
// This is the empty IEnumerable that gets returned when employees is null
private static readonly IEnumerable<EmployeeModel> EmptyEmployees =
new EmployeeModel[0];
public IEnumerable<EmployeeModel> Employees
{
get
{
return employees ?? EmptyEmployees;
}
set {};
}
}
The code returns an empty array when employees variable is set to null. You can set employees to a collection of any type that implements IEnumerable<EmployeeModel>, or even to an array if you prefer. This is possible because you return by interface.
The flip side of this, of course, is that the clients would have no direct access to methods of properties that are not exposed through the interface. For example, if employees is actually a List, the callers would have to use LINQ's Count() instead of obtaining .Count directly. Of course you can expose a different interface, say, IList<EmployeeModel>, to let your clients use additional methods.
You still need to provide an internal backing collection for the property in your class. You can initialize the collection in the constructor, or in the field declaration:
public class HumanResource
{
private readonly IList<EmployeeModel> _employees = new List<EmployeeModel>();
public IEnumerable<EmployeeModel> Employees
{
get
{
return _employees;
}
// No Setter - callers may only enumerate the collection
}
}
As an aside, note that even if you did use an automatic property (e.g. List<EmployeeModel>), that it would assume a default value of null, unless otherwise initialized elsewhere, so nothing changes in this respect.
Edit, Re : What are the benefits?
By removing the setter, or making it private, we prevent a caller from reassigning the internal collection of a HumanResource
By softening the collection from a List<> to an IEnumerable<>, it means the caller can only do read-only actions on the internal collection, such as to iterate it. In addition, IEnumerable<> can be used in a lazy iteration, allowing the caller to quit enumerating as soon as it has the data it needs.
As per the comment below, if the caller requires the data represented in a different collection, such as an Array, then LINQ extension methods such as .ToArray(), .ToList(), .ToDictionary() can be used. Doing so will create new collections for the caller, but with references to the same EmployeeModel objects. The performance penalties of doing this are minimal.
One final note is that there is usually no point in making the setter on an IEnumerable property private, or declaring the backing field as an IEnumerable, as this will prevent the class itself from using impure methods to manipulate the collection (i.e. add or remove objects from it), as doing so would require a cast, e.g.:
public class HumanResource
{
public IEnumerable<EmployeeModel> Employees
{
get;
private set;
}
public HumanResource()
{
// Although the property can be assigned in the CTor to prevent the null issue ...
Employees = new List<EmployeeModel>();
}
private void AddProductiveEmployee()
{
// ... We need to continually cast within the class, which is just silly.
(Employees as IList).Add(new EmployeeModel());
}
We would have the same problem with the manual backing field approach with an internal IEnumerable<>
// Unlikely to be useful
private readonly IEnumerable<EmployeeModel> _employees = new List<EmployeeModel>();
TL;DR
Use a collection type which is appropriate for the internal usage to the class (OO composition)
But on the external interfaces (e.g. public getter / property), hide the internal implementation to the minimum necessary (OO encapsulation)
Initializing the internal collection in the constructor (or inline) will prevent a null collection being exposed

How to cast list collection to derived collection object?

I have created a derived collection object to introduce some added functionality to filter the active records in the collection as shown in the below code snippet. How to achieve it as i want to just filter the same collection while keeping the original references in the filter without creating copy.
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes Active
{
get { return this.Where(x => x.IsActive).ToList(); } // Compile Error
}
}
Filtering an existing list
You mentioned that you wanted to just filter the existing list without keeping a copy. In this case, creating a List won't do, since creating a list from the subset will always create a new collection, not just a filter. List<T> is not a lazily-evaluated collection.
What you probably need to do is either define Active as IEnumerable<ExtendedType> and return the result of the Where directly (using LINQ's lazy implementation), or, if you're in WPF, use something like CollectionView as an additional filter on top of a collection, like this:
public ICollectionView ActiveTypes
{
get
{
if (_activeTypes == null)
{
_activeTypes = CollectionViewSource.GetDefaultView(myExtendedTypes);
_activeTypes.Filter = (type) => (type as ExtendedType).IsActive;
}
return _activeTypes;
}
}
You can now bind to ActiveTypes and get only a subset of the original list, filtered by the result of the Filter clause.
Creating a new List
However, assuming ExtendedType is a Reference type, you don't have to worry about copies of the items themselves being made by duplicating the list. If you don't mind creating a copy of the list with the same references, use my original answer:
The compiler is correct, in the sense that an ExtendedTypes is-a List<ExtendedType>, but not the other way around, and ToList() create a List<ExtendedType>.
There is, however, a simple workaround. Rather than ToList, just create a new ExtendedTypes with a constructor that initializes from a collection:
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes (IEnumerable<ExtendedType> items) : base(items)
{}
public ExtendedTypes Active
{
get { return new ExtendedTypes(this.Where(x => x.IsActive)); }
}
}

Exposing array-like data structure using properties

Its typical to expose internal data structures as properties to your business class. But when we have to expose array-like structures (like List<Rule> rules) we might encounter a problem of wrong usage (as in Option 1).
Its suggested to expose clone of such data structures as properties, so that internal structure does not get disturbed.
Does any one have a well-settled solution for this?
public class Rule
{
}
public class RulesManager
{
List<Rule> rules = new List<Rule>();
public List<Rule> Rules
{
get { return rules; }
set { rules = value; }
}
public void Add(Rule r)
{
rules.Add(r);
// Do something else after add;
}
public void Delete(Rule r)
{
rules.Remove(r);
// Do something else after delete;
}
}
public class CallingCode
{
public static void Main()
{
RulesManager r = new RulesManager();
// Option 1
r.Rules.Add(new Rule());
// Option 2
r.Add(new Rule());
}
}
Instead of returning a Clone, you can return a read-only version of rules using rules.AsReadOnly().
public IList<Rule> Rules
{
get { return rules.AsReadOnly(); }
// set { rules = value; -- should not be allowed to set if read only!
}
Note the IList.
Instead of returning a List you can return an IEnumerable. The IEnumerable allows the user to iterate through the collection, but it doesn't allow the user to modify it easily.
Alternatively you could return an arryay instead of a list. This will create a copy of the list that the user cannot easily modify.
Finally you should be aware that the user might also modify the contents of the collection. This may be what you want, but you might also want to return copies of your items.
I think it is quite common to expose the IList as a property, but I prefer to expose only explicit Add/Delete functions. You can also consider to implement one of the collection interfaces in your class (IList for instance), if you are developing something more of a framework.
Instead of:
public List<Rule> Rules
{
get { return rules; }
set { rules = value; }
}
I prefer to implement IEnumerable<T> and an indexer on the class, so that I have control over what happens to the list.
Check out the ReadOnlyCollection and the AsReadOnly() List method.
A basic workaround is to use List<T>.AsReadOnly() method, which will wrap the list around a ReadOnlyCollection to block any "write" access. Of course, you'd have to make the setter private, otherwise it would not make sense...
Another alternative would be to implement your own IList that would alert you in case of "write" access and allow you to perform your business logic.

Categories