views:

1778

answers:

5

I have a Queue<T> object that I have initialised to a capacity of 2, but obviously that is just the capacity and it keeps expanding as I add items. Is there already an object that automatically dequeues an item when the limit is reached, or is the best solution to create my own inherited class?

A: 

Why wouldn't you just use an array with a size of 2? A Queue is supped to be able to dynamically grow and shrink.

Or create a wrapper class around an instance of Queue instance and each time one enqueues a object, check the size of the queue. If larger than 2, dequeue the first item.

CodingWithoutComments
A: 

@mharen: Exactly, I'm basically talking about a class that acts exactly like a Queue but with automatic dequeuing of the first item when a specified limit is reached. I'd rather use a framework class is there is one but googling revealed nothing. I didn't think it was too much of an ask!

tags2k
+2  A: 

You should create your own class, a ringbuffer would probably fit your needs.

The data structures in .NET that allows you to specify capacity, except for array, uses this to build the internal data structure used to hold the internal data.

For instance, for a list, capacity is used to size an internal array. When you start adding elements to the list, it'll start filling this array from index 0 and up, and when it reaches your capacity, it increases the capacity to a new higher capacity, and continues filling it up.

Lasse V. Karlsen
+7  A: 

I've knocked up a basic version of what I'm looking for, it's not perfect but it'll do the job until something better comes along.

public class LimitedQueue<T> : Queue<T>
{
private int limit = -1;

public int Limit
{
get { return limit; }
set { limit = value; }
}

public LimitedQueue(int limit)
: base(limit)
{
this.Limit = limit;
}

public new void Enqueue(T item)
{
if (this.Count >= this.Limit)
{
this.Dequeue();
}
base.Enqueue(item);
}
}
tags2k
I augmented the code slightly with a call from within the Set of the Limit property that ensures that the Queue size hasn't exceeded the Limit - just a simple While greater than Limit, Dequeue.Other than that, this is a great solution thats nice and simple, thanks.
Scott
Good pickup on changing the 'setter' code for the 'Limit' property.
Pure.Krome
There's a very serious limitation to this class, which Marcus Griep hinted at in his answer: since your `Enqueue` method is declared as `new` (because `Queue<T>.Enqueue` is not virtual), if somebody casts your `LimitedQueue<T>` to a `Queue<T>` they'll be able to add as many items as they want without your limit taking effect. I would also recommend changing `if (this.Count >= this.Limit)` to `while (this.Count >= this.Limit)`, just to be on the safe side (for the scenario I just mentioned, for example).
Dan Tao
+4  A: 

I would recommend that you pull up the C5 Library. Unlike SCG (System.Collections.Generic), C5 is programmed to interface and designed to be subclassed. Most public methods are virtual and none of the classes are sealed. This way, you won't have to use that icky "new" keyword which wouldn't trigger if your LimitedQueue<T> were cast to a SCG.Queue<T>. With C5 and using close to the same code as you had before, you would derive from the CircularQueue<T>. The CircularQueue<T> actually implements both a stack and a queue, so you can get both options with a limit nearly for free. I've rewritten it below with some 3.5 constructs:

using C5;

public class LimitedQueue<T> : CircularQueue<T>
{
    public int Limit { get; set; }

    public LimitedQueue(int limit) : base(limit)
    {
        this.Limit = limit;
    }

    public override void Push(T item)
    {
        CheckLimit(false);
        base.Push(item);
    }

    public override void Enqueue(T item)
    {
        CheckLimit(true);
        base.Enqueue(item);
    }

    protected virtual void CheckLimit(bool enqueue)
    {
        while (this.Count >= this.Limit)
        {
            if (enqueue)
            {
                this.Dequeue();
            }
            else
            {
                this.Pop();
            }
        }
    }
}

I think that this code should do exactly what you were looking for.

Marcus Griep