overriding abstract generic method from non generic class

21,029

Solution 1

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.

Solution 2

abstract method should have this signeture

  public abstract void Draw<T>(List<T> type);  

Solution 3

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.

Share:
21,029
Whiplash450
Author by

Whiplash450

Updated on July 09, 2022

Comments

  • Whiplash450
    Whiplash450 almost 2 years

    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
    Whiplash450 about 12 years
    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'?
  • George Duckett
    George Duckett about 12 years
    I suggest making the base class have the generic parameter, then derived classes could implement Drawer<int> or Drawer<Agent> etc.
  • Whiplash450
    Whiplash450 about 12 years
    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<Drawer<Type>> drawerList). What can I put that would be a generic 'type' or is there any way around this??
  • phoog
    phoog about 12 years
    @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: stackoverflow.com/q/9115593/385844. There are a few possible solutions, but none of them is as slick as you'd hope for.
  • phoog
    phoog about 12 years
    @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?
  • Whiplash450
    Whiplash450 about 12 years
    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
    Whiplash450 about 12 years
    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?
  • phoog
    phoog about 12 years
    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<IDrawer> (or List<DrawerBase>) for the purpose of calling Draw() on each item in the list.
  • Whiplash450
    Whiplash450 about 12 years
    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.
  • phoog
    phoog about 12 years
    @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
    phoog about 12 years
    @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.