shinyhappydan
shinyhappydan

Reputation: 728

How do I override List<T>'s Add method in C#?

I am currently looking to make my own collection, which would be just like a regular list, except that it would only hold 10 items. If an item was added when there were already 10 items in the list, then the first item would be removed before the new item was appended.

What I want to do is create a class that extends System.Collections.Generic.List<T>, and then modifies the Add(T item) method to include the functionality which removes the first item if necessary.

Upvotes: 42

Views: 88440

Answers (11)

shinyhappydan
shinyhappydan

Reputation: 728

It seems like the best I can do is this:

class MostRecentList<T> : System.Collections.Generic.List<T> 
{
    private int capacity;

    public MostRecentList(int capacity) : base() 
    {
        this.capacity = capacity;
    }

    public new void Add(T item) 
    {
        if (base.Count == capacity)
        {
            base.RemoveAt(0);
        }
        base.Add(item);
    }
}

Since the add() method is not marked as virtual.

Upvotes: 4

b_scored
b_scored

Reputation: 21

It actually could be done, if Explicit Interface Implementation used.

public class SampleClass<T> : IList<T>
{
    ...
    void IList<T>.Add(T item) { ... }
    public int Add(T item) { ... }
    ...
}

Then called, like

IList<T> sample = new SampleClass();
int index = sample.Add(someStuffGoesHere); // new Add method is called

Upvotes: 1

bastio84
bastio84

Reputation: 101

You can try to extend System.Collections.ObjectModel.Collection<T>, which is much more flexible. You can then override the protected members InsertItem and SetItem, to customize the behaviour of your collection.

Upvotes: 0

to StackOverflow
to StackOverflow

Reputation: 124726

Your description of your requirement sounds like a Circular Buffer.

I implemented my own - similar to this implementation on CodePlex except that mine implements IList<T>.

Some of the other answers suggest using a Queue<T> - but this isn't quite the same thing, as it only allows FIFO access.

As a general point, it's not recommended to derive from List<T> - instead derive from Collection<T>, and implement any additional stuff you need. But for a circular buffer it's probably more appropriate to use a private array rather than deriving from Collection<T> like the CodePlex implementation.

Upvotes: 3

JohannesH
JohannesH

Reputation: 6450

You could take a look at the C5 collection library. They have an ArrayList<T> that implements IList<T> and have a virtual Add method. The C5 collection library is an awesome collection of lists, queues, stacks etc... You can find the C5 library here:

http://www.itu.dk/research/c5/

Upvotes: 1

Lee
Lee

Reputation: 1115

You could extend System.Collections.ObjectModel.Collection and override the InsertItem method to get the behaviour you want, and it also implements IList

Upvotes: 22

ShuggyCoUk
ShuggyCoUk

Reputation: 36438

Have a read of the Liskov Substitution Principle, your collection is a very poor candidate to extend List<T>, it is not even a great candidate to implement IList<T>.

What read patterns are required on this data? If you only need to look at all the current entries then implementing IEnumerable<T> and the Add(T) method should be sufficient to start with.

This can then be implemented by a private Queue (or Deque would be better but such a collection would require some other collections API and I do not suggest you try to implement one yourself) to which you Enqueue() during an Add (with Dequeue's if needed to maintain the size).

Note that implementing IEnumerable and providing the Add method means you can still use the Collection initialiser syntax if required.

If you need random access to the values then implementing an indexer may be a good idea but I don't see what benefit this would give you without more context on the question.

Upvotes: 1

Quintin Robinson
Quintin Robinson

Reputation: 82345

You can also implement the add method via

public new void Add(...)

in your derived class to hide the existing add and introduce your functionality.

Edit: Rough Outline...

class MyHappyList<T> : List<T>
{
    public new void Add(T item)
    {
        if (Count > 9)
        {
            Remove(this[0]);
        }

        base.Add(item);
    }
}

Just a note, figured it was implied but you must always reference your custom list by the actual type and never by the base type/interface as the hiding method is only available to your type and further derived types.

Upvotes: 79

Randolpho
Randolpho

Reputation: 56391

First, you can't override Add and still have polymorphism against List, meaning that if you use the new keyword and your class is cast as a List, your new Add method won't be called.

Second, I suggest you look into the Queue class, as what you are trying to do is more of a queue than it is a list. The class is optimized for exactly what you want to do, but does not have any sort of a size limiter.

If you really want something to act like a List but work like a Queue with a maximum size, I suggest you implement IList and keep an instance of a Queue to store your elements.

For example:

public class LimitedQueue<T> : IList<T>
{
  public int MaxSize {get; set;}
  private Queue<T> Items = new Queue<T>();
  public void Add(T item)
  {
    Items.Enqueue(item);
    if(Items.Count == MaxSize)
    {
       Items.Dequeue();
    }
  }
  // I'll let you do the rest
}

Upvotes: 43

Ryan Emerle
Ryan Emerle

Reputation: 15811

You can just write a class that implements IList<T> that holds an internal List<T> and write your own methods.

Upvotes: 10

Hans Passant
Hans Passant

Reputation: 941635

You can't override Add(), it is not a virtual method. Derive from IList instead and use a private Queue member for the implementation.

Upvotes: 27

Related Questions