11

I'm a relative newbie to C#, although I am a competent programmer, and I confess that I am totally confused as to whether or not it is a good idea to write custom collection classes. So many people seem to say "don't", yet there is a whole set of base classes for it in C#.

Here is my specific case. I have a timetable application. As part of that, I have a service class, and the service class contains collections of things service-y, such as route links. A route link is itself a custom class:

public class Service
{
  public RouteLinks RL;    // A collection of RouteLink types
  ...
}

public class RouteLink
{
    public string FirstStopRef;
    public string LastStopRef;
    public Tracks RouteTrack;    // Another collection, this time of Track types
}

So far I have looked at using Dictionary as the type for RouteLinks, because I need to be able to reference them. This is fine in principle. However, the process of adding a RouteLink to the RouteLinks collection involves checking to see whether it is already there, or whether it extends and existing route link, or... And for that, I need a custom Add function.

So why is is such bad practice to create custom collection classes? Why shouldn't I just inherit CollectionBase or DictionaryBase?

I should perhaps add that I am transferring this code from VBA [please don't shoot me :)] and there I HAD to implement custom collections.

StuartR143
  • 369
  • 2
  • 10
  • I don't normally create custom collections, but I can see your point and would like to see other people's opinions on this. Extension methods perhaps? – Polyfun Mar 10 '14 at 10:18
  • 7
    Even if you do choose to implement a collection - don't do it from scratch. Instead, wrap an internal standard collection. – Benesh Mar 10 '14 at 10:18
  • My first attempt would be to write an extension method "MyAdd()" for your dictionary. http://msdn.microsoft.com/en-us//library/bb383977.aspx – elnigno Mar 10 '14 at 10:20
  • 5
    `So why is is such bad practice to create custom collection classes?` In and of itself, it is not. That's why they're not sealed. It's only bad practice if you inherit from collection classes to implement a business object - as opposed to a (possibly improved) *mechanism*. See http://stackoverflow.com/questions/21692193/why-not-inherit-from-listt - Eric Lippert's answer especially. The distinction between a mechanism and a business object nails it down. – Konrad Morawski Mar 10 '14 at 10:20
  • @KonradMorawski, oh yeah, I remember this article. Good one. – Andrew Savinykh Mar 10 '14 at 10:22
  • `So far I have looked at using Dictionary as the type for RouteLinks, because I need to be able to reference them.` - You can reference any type, `Dictionary` is not different. – Maarten Mar 10 '14 at 10:23
  • @KonradMorawski Thank - that's certainly an informative answer, and there were other good nuggets in the comments as well – StuartR143 Mar 10 '14 at 11:01

2 Answers2

1

Instead of having RouteLinks be a collection type, an easy solution would be to just define another class, let's say RouteLinksRepository. This class will contain a List<RouteLink> and the AddRoute(RouteLink) functionality as well as any other custom logic for interacting with this collection of RouteLink objects. Your service class will then just contain an instance of this repository class.

public class Service
{
  public RouteLinksRepository RL;    // A collection of RouteLink types
  // ...
}

public class RouteLinksRepository
{
    public List<RouteLink> RouteLinks;
    public bool AddRoute(RouteLink linkToAdd)
    {
        //Custom logic on whether or not to add link
    }
    //Your other logic for the class

}

public class RouteLink
{
    public string FirstStopRef;
    public string LastStopRef;
    public Tracks RouteTrack;    // Another collection, this time of Track types
}
Daniel Simpkins
  • 674
  • 5
  • 18
0

If the only need is to check on double entries, a HashSet will do (implement a GetHash and Equals). However I guess you are trying to save a route. A route has a order, which means you have a order and List<> garantees the order. Make the collection objects private to hide the implementation.

public class Service
{
  private List<RouteLink> RL;    // A collection of RouteLink types
  ...
}

public class RouteLink
{
    public string FirstStopRef;
    public string LastStopRef;
    private List<Track> Tracks;    // Another collection, this time of Track types
}
Peter
  • 27,590
  • 8
  • 64
  • 84
  • It's rather more than just checking on double entries - I simplified it greatly for the question. But thanks, will have to read up more about HashSet as I can see that it might be useful in other use cases. – StuartR143 Mar 10 '14 at 11:22
  • @StuartR143 bear in mind that implementing your own collection class can be tricky to get it right. There's a few important caveats to bear in mind, such as thread-safety. There's a rich ecosystem of opensource .NET projects so if you need some enhanced functionality that standard .NET libraries don't give you, I'd take a look at ready-made options such as http://www.itu.dk/research/c5 before snapping out my own solutions. – Konrad Morawski Mar 10 '14 at 12:12
  • @KonradMorawski Thanks - another interesting link. Given what I read in the article you linked to in my OP, I think that I am looking at fairly standard collections, and that what I mostly need to do is to provide a customised Add method for a particular need. So I am writing business objects, rather than mechanisms, to borrow your terminology. Lots of incredibly useful pointers to think about. – StuartR143 Mar 10 '14 at 16:07