Inheriting from List<T>

Why not inherit from ListT?

There are some good answers here. I would add to them the following points.

What is the correct C# way of representing a data structure, which, "logically" (that is to say, "to the human mind") is just a list of things with a few bells and whistles?

Ask any ten non-computer-programmer people who are familiar with the existence of football to fill in the blank:

A football team is a particular kind of _____

Did anyone say "list of football players with a few bells and whistles", or did they all say "sports team" or "club" or "organization"? Your notion that a football team is a particular kind of list of players is in your human mind and your human mind alone.

List<T> is a mechanism. Football team is a business object -- that is, an object that represents some concept that is in the business domain of the program. Don't mix those! A football team is a kind of team; it has a roster, a roster is a list of players. A roster is not a particular kind of list of players. A roster is a list of players. So make a property called Roster that is a List<Player>. And make it ReadOnlyList<Player> while you're at it, unless you believe that everyone who knows about a football team gets to delete players from the roster.

Is inheriting from List<T> always unacceptable?

Unacceptable to whom? Me? No.

When is it acceptable?

When you're building a mechanism that extends the List<T> mechanism.

What must a programmer consider, when deciding whether to inherit from List<T> or not?

Am I building a mechanism or a business object?

But that's a lot of code! What do I get for all that work?

You spent more time typing up your question that it would have taken you to write forwarding methods for the relevant members of List<T> fifty times over. You're clearly not afraid of verbosity, and we are talking about a very small amount of code here; this is a few minutes work.

UPDATE

I gave it some more thought and there is another reason to not model a football team as a list of players. In fact it might be a bad idea to model a football team as having a list of players too. The problem with a team as/having a list of players is that what you've got is a snapshot of the team at a moment in time. I don't know what your business case is for this class, but if I had a class that represented a football team I would want to ask it questions like "how many Seahawks players missed games due to injury between 2003 and 2013?" or "What Denver player who previously played for another team had the largest year-over-year increase in yards ran?" or "Did the Piggers go all the way this year?"

That is, a football team seems to me to be well modeled as a collection of historical facts such as when a player was recruited, injured, retired, etc. Obviously the current player roster is an important fact that should probably be front-and-center, but there may be other interesting things you want to do with this object that require a more historical perspective.

Inheriting from ListT

If you want to create a publicly exposed animal collection you should not inherit from List<T> and instead inherit from Collection<T> and use the postfix Collection in the class name. Example: AnimalCollection : Collection<Animal>.

This is supported by the framework design guidelines, more specifically:

DO NOT use ArrayList, List<T>,
Hashtable, or Dictionary<K,V> in
public APIs. Use Collection<T>,
ReadOnlyCollection<T>,
KeyedCollection<K,T>, or
CollectionBase subtypes instead. Note
that the generic collections are only
supported in the Framework version 2.0
and above.

Inheriting ListT to implement collections a bad idea?

I wouldn't inherit from List<T> - it introduces issues like these, and doesn't really help (since there are no virtual methods to override). I would either use List<T> (or the more abstract IList<T>), or to introduce polymorphism Collection<T> has virtual methods.

As a note; re things like FindAll, you may also find the LINQ options (like .Where()) useful counterparts; most notably, they will work for any IList<T> (or IEnumerable<T>), not just List<T> and subclasses.

Question inheriting from List(of T) class

Your issue is that you're both inheriting from List(of T) and you have an instance property of that type, which is where you're storing your data. When Count is called in your above code, it's using the Count property from your parent List(of T), which isn't where you're storing your data.

A better idea would be for you to inherit from object and have PriorityQueue(of T) implement ICollection and IEnumerable(of T) explicitly. You shouldn't have to change your internal implementation at all, you'll just have to add code to support those interfaces.

Inheriting from ListT in .NET (vb or C#)

You said:

I have a class called 'Person' and I
need a collection called 'Scouts',
which I want in an Class called
'Scouts', at this point I'd like to be
able to write

foreach Person in Scouts ...

If I understand you correctly, you want:

class Scouts : IEnumerable<Person>
{
private List<Person> scouts;

public IEnumerator<Person> GetEnumerator()
{
return scouts.GetEnumerator();
}
}

C# Adding Current property to a list class inheriting from listT

There's a few options on how to do this. As you're just starting out, I'd say use option 1 as this is a lot simpler.

Option 1: Basic using LINQ

Below is a Volume class and a Main method which shows examples of usage. You can use Linq to get the first/last items in the list of volumes and then you can set properties on individual items in the list by using volumes[i] where i is the index of the item in the list.

You can also use Linq to find particular volumes based on Color, Name, HasVolume and then change properties on each of those using a foreach loop.

More info on LINQ is here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/.

Take a look at the example below:

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
var volumes = new List<Volume>();
var volume1 = new Volume("vol1", "red", true);
var volume2 = new Volume("vol2", "red", true);

volumes.Add(volume1); //adds the first volume
volumes.Add(volume2); //adds the second volume
var lastVolume = volumes.First(); //gets the first volume
var firstVolume = volumes.Last(); //gets the last volume
volumes[0].Color = "blue"; //sets the first volume color to blue
volumes[1].Color = "green"; //sets the second volume color to green

// set all green volumes to purple
var greenVolumes = volumes.Where(v => v.Color == "green");
foreach (var greenVolume in greenVolumes)
{
greenVolume.Color = "purple";
}
}
}

public class Volume
{
public string Name { get; set; }
public string Color { get; set; }
public bool HasSegment { get; set; }

public Volume(string name, string color, bool hasSegment)
{
Name = name;
Color = color;
HasSegment = hasSegment;
}
}
}

Option 2: More advanced using the Iterator pattern

Take a look at the iterator pattern here: https://www.dofactory.com/net/iterator-design-pattern.

Basically, it will allow you to step through the list by selecting MoveNext(), Last, First, and Current. I've added a _current field which keeps track of which item you are working with. It relates to the index of the collection/list.

using System.Collections.Generic;

namespace WebApplication1.Sandbox
{
public class Volume
{
public string Name { get; set; }
public string Color { get; set; }
public bool HasSegment { get; set; }

public Volume(string name, string color, bool hasSegment)
{
Name = name;
Color = color;
HasSegment = hasSegment;
}
}

public class Iterator<T>
{
private readonly List<T> _items;
private int _index;

public T Last
{
get => _items[_items.Count - 1];
set => _items[_items.Count - 1] = value;
}

public T First
{
get => _items[0];
set => _items[0] = value;
}

public T Current
{
get => _items[_index];
set => _items[_index] = value;
}

public Iterator(List<T> items)
{
_items = items;
}

public bool MoveNext()
{
if (_index == _items.Count - 1)
{
return false;
}
else
{
_index++;
return true;
}
}
}
}

Now you can use it like in this unit test:

        [Fact]
public void VolumeIteratorGetsNextThenSetsCurrentByProperty()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current.Name = "vol2modifiedAgain";

Assert.Equal("vol2modifiedAgain", sut.Current.Name);
}

Unit tests are here:

using System.Collections.Generic;
using WebApplication1.Sandbox;
using Xunit;

namespace WebApplication1.Tests
{
public class VolumeIteratorTests
{
private List<Volume> _volumes => new List<Volume>()
{
new Volume("vol1", "green", true),
new Volume("vol2", "green", true)
};

[Fact]
public void VolumeIteratorGetsCurrent()
{
var sut = new Iterator<Volume>(_volumes);

Assert.Equal("vol1", sut.Current.Name);
}

[Fact]
public void VolumeIteratorGetsNext()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();

Assert.Equal("vol2", sut.Current.Name);
}

[Fact]
public void VolumeIteratorGetsNextThenSetsCurrent()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current = new Volume("vol2modified", "green", false);

Assert.Equal("vol2modified", sut.Current.Name);
}

[Fact]
public void VolumeIteratorGetsNextThenSetsCurrentByProperty()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current.Name = "vol2modifiedAgain";

Assert.Equal("vol2modifiedAgain", sut.Current.Name);
}

[Fact]
public void VolumeIteratorGetsLast()
{
var sut = new Iterator<Volume>(_volumes);

Assert.Equal("vol2", sut.Last.Name);
}

[Fact]
public void VolumeIteratorSetsLast()
{
var sut = new Iterator<Volume>(_volumes);
sut.Last = new Volume("last", "red", true);

Assert.Equal("last", sut.Last.Name);
}

[Fact]
public void VolumeIteratorGetsFirst()
{
var sut = new Iterator<Volume>(_volumes);

Assert.Equal("vol1", sut.First.Name);
}

[Fact]
public void VolumeIteratorSetsFirst()
{
var sut = new Iterator<Volume>(_volumes);
sut.First = new Volume("first", "red", true);

Assert.Equal("first", sut.First.Name);
}

[Fact]
public void MoveNextReturnsTrueIfNotLastItem()
{
var sut = new Iterator<Volume>(_volumes);

Assert.True(sut.MoveNext());
}

[Fact]
public void MoveNextReturnsFalseIfLastItem()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();

Assert.False(sut.MoveNext());
}
}
}


Related Topics



Leave a reply



Submit