I'm writing a class to represent a Pivot Collection, the root object recognized by Pivot. A Collection has several attributes, a list of facet categories (each represented by a FacetCategory object) and a list of items (each represented by a PivotItem object). Therefore, an extremely simplified Collection reads:
public class PivotCollection
{
private List<FacetCategory> categories;
private List<PivotItem> items;
// other attributes
}
What I'm unsure of is how to properly grant access to those two lists. Because declaration order of both facet categories and items is visible to the user, I can't use sets, but the class also shouldn't allow duplicate categories or items. Furthermore, I'd like to make the Collection object as easy to use as possible. So my choices are:
Have PivotCollection implement IList<PivotItem> and have accessor methods for FacetCategory: In this case, one would add an item to Collection foo by writing foo.Add(bar). This works, but since a Collection is equally both kinds of list making it only pass as a list for one type (category or item) seems like a subpar solution.
Create nested wrapper classes for List (CategoryList and ItemList). This has the advantage of making a consistent interface but the downside is that these properties would no longer be able to serve as lists (because I need to override the non-virtual Add method I have to implement IList rather than subclass List. Implicit casting wouldn't work because that would return the Add method to its normal behavior.
Also, for reasons I can't figure out, IList is missing an AddRange method...
public class PivotCollection
{
private class CategoryList: IList<FacetCategory>
{
// ...
}
private readonly CategoryList categories = new CategoryList();
private readonly ItemList items = new ItemList();
public CategoryList FacetCategories
{
get { return categories; }
set { categories.Clear(); categories.AddRange(value); }
}
public ItemList Items
{
get { return items; }
set { items.Clear(); items.AddRange(value); }
}
}
Finally, the third option is to combine options one and two, so that PivotCollection implements IList<PivotItem> and has a property FacetCategories.
Question: Which of these three is most appropriate, and why?
The best thing to do here is to create your own collection class that inherits System.Collections.ObjectModel.Collection<T> and overrides InsertItem.
Related
I have a class with a static list as shown below:
public class Context
{
private static readonly List<Definition> definitions;
static Context()
{
definitions = LoadXML("path-to-xml-file.xml"));
}
public static List<Definition> GetDefinitions()
{
return definitions;
}
}
My problem is making calls to GetDefinitions() seems to return the list by reference instead of by value, because when I do this elsewhere in my code:
var defs = Context.GetDefinitions().ToList();
defs.ForEach(a =>
{
a.Name = a.Alias ?? a.Name;
});
all subsequent calls to Context.GetDefinitions() will return the modified list - not the original one, hence my conclusion that defs is not a value but a reference to the definitions list.
I tried adding the .ToList() in an attempt to decouple the reference but still I get the same result.
I am also open to a workaround, which allows me to use .Select() instead of .ForEach() in my sample code.
The problem is that the list does not store the items itself, but rather references to the items. Even if you create a new list (e.g. with ToList()), the referenced items stay the same.
In order to fix this, you need to clone the items in the list so that you have a independent copy of the data. You can implement ICloneable on the items and use return the list like this:
public static List<Definition> GetDefinitions()
{
return definitions.Select(x => (Definition)x.Clone()).ToList();
}
This way you create a new list that contains the cloned items. However, cloning an item is a tedious task if you need to clone a deeply nested class structure. For a flat structure, using MemberwiseClone is an easy way.
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.
Here it goes my question:
I intend to create several lists of objects, being each of the objects from the same class.
However, within each of the lists, I would like the different instances of the objects to share a common member I could change from any of these instances.
Of course, each of the lists have its own "static" member.
I hope I had explained myself, I am beginner and I am not sure whether my question has an obvious solution.
Edit:
I am working on an existing code, and I make every step by doing a trial-error approach. I do not know exactly how to explain it better: I have List list1, and List list2. The set of instances within list1 will have members with different values, but I want to have a member in class A which can be modified from any instance within the list1 and to be common to all the instances in list1. Similarly for list2, but with a different "static" member that the one in list1.
I think this will roughly achieve what you are looking to do. There are many ways to change the structure of the code below to achieve different goals but basically what you will need to do is create some sort of custom list that when items are added to it, it attaches itself to those items, and removes itself from them when they are removed.
public class CustomList : IList<IItem>
{
public Object CommonMember { get; set; }
private List<IItem> _internalList = new List<IItem>();
public void Add(IItem item)
{
item.OwnedList = this;
this._internalList.Add(item);
}
public void Remove(IItem item)
{
if(this._internalList.Remove(item))
{ item.OwnedList = null; }
}
... you will need to implment more members
}
public abstract class IItem
{
public Object OwnedListCommonMember
{
get {
if(this.OwnedList != null)
{ return this.OwnedList.CommonMember; }
else { return null; }
}
}
public CustomList OwnedList { get; set; }
}
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
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)); }
}
}