Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have the following:

public interface IBehaviour
{
    event EventHandler Completed;
    void Execute();
}

public interface IBehaviourA : IBehaviour
{
    // Some specific stuff here
    object A { get; set; }
}

public interface IBehaviourB : IBehaviour
{
    // Some specific stuff here
    object B {get;set;}
}

public interface IBehaviourQueue
{
    void Run();
    BehaviourQueueItem<IBehaviour> AddBehaviour<T>() where T : IBehaviour;
}

public class BehaviourQueue : Queue<BehaviourQueueItem<IBehaviour>>,  IBehaviourQueue
{
    private IBehaviourFactory factory;
    public BehaviourQueue(IBehaviourFactory factory)
    {
        this.factory = factory;
    }

    public BehaviourQueueItem<IBehaviour> AddBehaviour<T>() where T:IBehaviour
    {
        T behaviour = factory.GetNew<T>();
        var queueItem = new BehaviourQueueItem<IBehaviour>(behaviour);
        Enqueue(queueItem); 
        return queueItem;
    }

    public void Run()
    {
        //Run each queue item
    }
}

public class BehaviourQueueItem<T>
{
    public IBehaviour behaviour;

    public BehaviourQueueItem(IBehaviour behaviour)
    {
        this.behaviour = behaviour;
    }

    public void WhenComplete(Func<T, bool> action) 
    {
        CompletedAction = action;
    }

    public BehaviourQueueItem<T> ConfigureFor<Z>(Action<Z> dow) where Z : IBehaviour
    {
        dow((Z)behaviour);
        return this;
    }
}

This is how it is used:

var q =new BehaviourQueue(new BehaviourFactory()); // Queue here is IBehaviour
q.AddBehaviour<IBehaviourA>()
 .ConfigureFor<IBehaviourA>(x => x.A = "someValueA")
 .WhenComplete(x => DoStuffWithSomeProperty(((IBehaviourA)x).A));

q.AddBehaviour<IBehaviourB >()
 .ConfigureFor<IBehaviourB >(x => x.B = "someValueB")
 .WhenComplete(x => DoStuffWithSomeProperty(((IBehaviourB)x).B));

What I don't really like is that I have to specify which type of IBehaviour I am referring to every time. I would like to be able to write:

var q =new BehaviourQueue(new BehaviourFactory()); // Queue here is IBehaviour
q.AddBehaviour<IBehaviourA>()
 .Configure(x => x.A = "someValueA")
 .WhenComplete(x => DoStuffWithSomeProperty(x.A));

q.AddBehaviour<IBehaviourB>()
 .Configure(x => x.B = "someValueB")
 .WhenComplete(x => DoStuffWithSomeProperty(x.B));

I know that I have to modify this part:

public BehaviourQueueItem<IBehaviour> AddBehaviour<T>() where T:IBehaviour
{
    T behaviour = factory.GetNew<T>();
    var queueItem = new BehaviourQueueItem<IBehaviour>(behaviour);
    Enqueue(queueItem);
    return queueItem;
}

to something like

public BehaviourQueueItem<T> AddBehaviour<T>() where T:IBehaviour
{
    T behaviour = factory.GetNew<T>();
    var queueItem = new BehaviourQueueItem<T>(behaviour);
    Enqueue(queueItem); // It won't let me know do it because of Covariance/Contravariance thingy
    return queueItem;
}

Do you have any idea what I should write to be able to create a base typed list and add specific items and configuring it fluently?

Keep in mind that I should be able to add any type of IBehaviour to BehaviourQueue so the queue can't be a derived type.

share|improve this question

put on hold as off-topic by 200_success Sep 11 at 6:46

This question appears to be off-topic. The users who voted to close gave this specific reason:

If this question can be reworded to fit the rules in the help center, please edit the question.

    
You will perhaps be interested in how other projects try to handle that, I found that one: flit.codeplex.com –  Guillaume86 Dec 28 '11 at 11:13

2 Answers 2

up vote 1 down vote accepted

One possible workaround would be to declare the BehaviourQueue class as being Queue<object> instead of Queue<BehaviourQueueItem<IBehaviour>>.

This will allow you to define the AddBehaviour<T>() method the way you want:

public BehaviourQueueItem<T> AddBehaviour<T>() where T : IBehaviour
{
    T behaviour = factory.GetNew<T>();
    var queueItem = new BehaviourQueueItem<T>(behaviour);
    Enqueue(queueItem); // no problem here now...
    return queueItem;
}

Furthermore, you'll be able to change the ConfigureFor method to:

public BehaviourQueueItem<T> Configure(Action<T> dow)
{
    dow((T)behaviour);
    return this;
}

and now the configuration looks like this:

q.AddBehaviour<IBehaviourA>()
    .Configure(x => x.A = "someValueA")
    .WhenComplete(x => DoStuffWithSomeProperty(x.A));

q.AddBehaviour<IBehaviourB>()
    .Configure(x => x.B = "someValueB")
    .WhenComplete(x => DoStuffWithSomeProperty(x.B));

The only problem that remains is the dirty Queue<object> thing, meaning that the Dequeued objects will need to be cast back to BehaviourQueueItem<IBehaviour>.

It's up to you to decide whether that's an acceptable compromise.

However, looking at the code I see that the actual Queue implementation is not meant to be used by the class's clients. Clients don't call Enqueue directly, but AddBehaviour (which uses Enqueue internally). Similarly, I could male the assumption that Dequeue is only called by the Run() method, when it processes the items. If this is the case, the BehaviourQueue class may not even expose the Queue interface; it could instead just have a Queue<object> field that does the queue job and only expose the AddBehaviour and Run public methods.

share|improve this answer
    
Excellent answer. –  JeanT Feb 3 '12 at 23:30
    
After applying the changes I had problem casting IBehaviourQueueItem<IBehaviour> back to IBehaviourQueueItem<IBehaviourA>. Making T covariant on IBehaviourQueueItem interface made it work. public interface IBehaviourQueueItem<out T> –  JeanT Feb 4 '12 at 21:10

If I understand it right, you would want IBehaviourA associated with property A, and IBehaviourB with property B.

If the above is right, could you try something like:

BehaviourQueueItem<T> AddBehaviour() where T : IBehaviour;

instead of

BehaviourQueueItem<IBehaviour> AddBehaviour<T>() where T : IBehaviour;

By this we can determine the object type at BehaviourQueueItem class level itself.

share|improve this answer
    
If I do this, I cannot choose what type of IBehaviour I want to add and that would force me to make the type of queue to IBehaviourA Or B. –  JeanT Jan 1 '12 at 13:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.