9

base class

class Drawer
{
    public abstract void Draw<T>(T type);    
}

derived class #1

class ADrawer : Drawer
{
    public override void Draw<T>(List<T> list)
    {
        foreach (var a in list)
        {
            DrawA(a);
        }
    }

    public void DrawA(Agent a)
    {
        //draw code here
    }
}

derived class #2

class AnotherDrawer : Drawer
{
    public override void Draw<T>(T number)
    {
        if (number == 1)
        {
            //draw code
        }
    }
}

The error is in the #1 derived class : "no suitable method found to override"

Should I be using 'virtual' in the base class as well as 'abstract' ?

How should I set the base parameter type to allow a variety of parameters in derived classes?

Whiplash450
  • 943
  • 2
  • 12
  • 22
  • Just a side note, an abstract method is implicitly a virtual method so you shouldn't ever have to define a method as both abstract AND virtual. – docmanhattan Feb 08 '12 at 16:34
  • 2
    You have two conceptually different methods. One of your "Draw" methods *draws a thing*, and the other *draws many things*. You shouldn't be trying to make them into the same method in the first place; make *two* methods: `Draw(T item)` and `DrawMany(IEnumerable items)`. Same as `List` has `Add` and `AddRange` methods; doing something to a single thing and doing something to many things are two different operations, so have two different methods. – Eric Lippert Feb 08 '12 at 17:09

3 Answers3

12

Your code has more problems than just the one you ask about. Setting aside the override question for the moment, class ADrawer needs a type constraint (where T : Agent):

class ADrawer : Drawer 
{ 
    public void Draw<T>(List<T> list) where T : Agent
    { 
        foreach (var a in list) 
        { 
            DrawA(a); 
        } 
    }
    public void DrawA(Agent a) 
    { 
        //draw code here 
    } 
} 

Without that constraint, it's not legal to pass a to DrawA, because a is a reference of type T, and without the constraint there is no implicit conversion from type T to type Agent.

The AnotherDrawer class has an illegal use of the == operator. It's not possible to apply the == operator to operands of type T and int. You could get around that by using the object.Equals override.

Finally, the base class has an error because it is a non-abstract class containing an abstract member.

In general, however, this code indicates that the class should be generic, rather than the method:

abstract class Drawer<T>
{
    public abstract void Draw(T type);
}

derived class #1

class ADrawer : Drawer<List<Agent>>
{
    public override void Draw(List<Agent> list)
    {
        foreach (var a in list)
        {
            DrawA(a);
        }
    }       

    public void DrawA(Agent a)
    {
        //draw code here
    }
}

derived class #2

class AnotherDrawer : Drawer<int>
{
    public override void Draw(int number)
    {
        if (number == 1)
        {
            //draw code
        }
    }
}

To follow up on Eric Lippert's comment, which was also my first reaction to your question, you might consider this design instead:

abstract class Drawer<T>
{
    public abstract void Draw(T type);
    public void DrawMany(IEnumerable<T> types)
    {
        foreach (var t in types)
            Draw(t);
    }
}

derived class #1

class ADrawer : Drawer<Agent>
{
    public override void DrawA(Agent a)
    {
        //draw code here
    }
}

Derived class #2 is unchanged.

phoog
  • 42,068
  • 6
  • 79
  • 117
  • Thank you very much for a very useful answer. I have made the base class 'Abstract' now, and everything compiles. My only problem now is that when I create a list of Drawer's I now have to specify a type (e.g `List> drawerList`). What can I put that would be a generic 'type' or is there any way around this?? – Whiplash450 Feb 09 '12 at 15:35
  • @Whiplash450 you want to have a list of drawers, where the drawers draw different types of objects? If so, look at this question and my answer to it: http://stackoverflow.com/q/9115593/385844. There are a few possible solutions, but none of them is as slick as you'd hope for. – phoog Feb 09 '12 at 15:48
  • @Whiplash450 The best solution would depend a bit on the context in which you're getting the objects to be drawn. Are they also in a collection that you're iterating (in which case the reference to the objects would be of a common base type)? Or do you have a more specific static reference to an object, and you need to get the corrrect drawer for that reference? – phoog Feb 09 '12 at 15:54
  • The point of the separate drawers is so that they each have a single responsibility for drawing one element type each (ie. agent, node etc..). These drawers take a list of objects passed at runtime. But there are other drawers that don't and just get passed an int(for example). I would like to hold all drawers in a list so that they can all be updated in one loop. – Whiplash450 Feb 09 '12 at 16:13
  • Due to them taking different parameters in their Draw methods, the draw calls have to be made separately (which is unfortunate but unavoidable I believe). As the base Drawer class is now 'typed', does this mean the only way to store all drawers is in a custom List class? – Whiplash450 Feb 09 '12 at 16:13
  • If the drawers can keep private references to the objects they're drawing, they could implement a non-generic interface (or base class) with a parameterless `Draw()` method; then you can store them in a `List` (or `List`) for the purpose of calling `Draw()` on each item in the list. – phoog Feb 09 '12 at 16:16
  • That is a possibility but it just swaps the problem round. Altering the draw method, as you suggested, would mean that to keep the private data current the update method would now have to take the private type as a parameter. So that solves one problem but creates another. A Messy solution to which would be creating a unique update method for each drawer. – Whiplash450 Feb 09 '12 at 16:19
  • @Whiplash450 if you read my answer to yesterday's question, I attempted to prove that the only solutions to this problem are variations on "swapping the problem around". I'm not yet good enough with type algebra to do a formal proof, but ultimately you want a collection that you can enumerate to draw objects of diverse types. Either all of the objects need to implement a common interface, perhaps as some variant of the visitor pattern, or you will need casts with runtime type checks. – phoog Feb 09 '12 at 16:26
  • @Whiplash450 and since one of the types you'd like to "draw" is `int`, the visitor pattern isn't of much help as you can't add members to that class (and extension methods are not dispatched virtually). So that leaves you with the run-time cast. – phoog Feb 09 '12 at 18:38
5

abstract method should have this signeture

  public abstract void Draw<T>(List<T> type);  
wiero
  • 2,176
  • 1
  • 19
  • 28
  • ah ok, but I have mutiple derived classes from the base type, that each take a different parameter type, so how to setup the base parameter 'generically'? – Whiplash450 Feb 08 '12 at 16:35
  • 1
    I suggest making the base **class** have the generic parameter, then derived classes could implement `Drawer` or `Drawer` etc. – George Duckett Feb 08 '12 at 16:59
1

To get it to compile change the base class to this:

class Drawer
{
    public abstract void Draw<T>(List<T> type);    
}

List<T> is not the same as T, so when you pass in a List<T> in the derived class' method you can't override the base method as that has a T parameter, not a List<T> parameter.

George Duckett
  • 31,770
  • 9
  • 95
  • 162