0

It's, I think somethin basic in OOP :

Environment : C#/.net 2.0

Let's say I have two class :

public class Animal
{

}
public class Dog : Animal
{

}

A service class with two method :

 public void DoStuff(Animal animal)
    {
        Console.Write("Animal stuff");
    }
    public void DoStuff(Dog animal)
    {
        Console.Write("Dog stuff");
    }

If I execute the following code :

 Animal instance = new Animal();
        MyService.DoStuff(instance);

        Animal instance2 = new Dog();
        MyService.DoStuff(instance2);

"Animal stuff" is printed twice.

So my question is : why ? And how can I get "Animal stuff" and "Dog stuff" without casting instance2, or moving the method from my service to my class (in fact I would like that my code works, but it's not :()

Thanks

PS : These are just example :)


Because the Visitor pattern is not really appealing, I'll just move my service's method to my class, waiting for a better solution.

remi bourgarel
  • 9,231
  • 4
  • 40
  • 73

3 Answers3

1

You aren't overriding the doStuff() method in Dog, because the parameter type is different. They're two separate methods.

Either change the signature in Dog to match Animal or create a Visitor that sorts it out for you.

Here's one way to write it:

public interface Animal {  void accept(AnimalVisitor visitor); }

public class AbstractAnimal : Animal
{
    public void accept(AnimalVisitor visitor) { visitor.visit(this); } 
}

public class Dog : AbstractAnimal {}

public class Cat : AbstractAnimal {}

public interface AnimalVisitor
{
    void visit(Animal animal);
    void visit(Dog dog);
    void visit(Cat cat);
}

Now the service (and all others) can implement AnimalVisitor and do different things to each Animal subtype.

It's a common pattern called "double dispatch"; you can read more about it in Scott Meyers' "More Effective C++".

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • doStuff is not a part of Dog or animal class, can you explain about the visitor ? – remi bourgarel Dec 23 '10 at 08:22
  • I just read more about this pattern and indeed it seems like a solution, but it has some default : 1/ it needs a lot of code to be implemented, 2/ we lost the benefit of the service class : we had a way to its method in the animal class, 3/ if you don't know this pattern you don't know what's going on ! – remi bourgarel Dec 23 '10 at 09:02
  • I think I know the pattern; you're the one that doesn't know what's going on. 1. "A lot of code" - I don't know where you got that idea. There's some code, to be sure, but not a lot. 2. Your Animal and Dog classes have no methods in them at all when I write this. I can't tell about "a way to the service method in the Animal class", because there is no implementation. 3. I don't know the pattern? You had to look it up. I'd say you're the one that doesn't know what's going on. – duffymo Dec 23 '10 at 11:56
  • @remi, I couldn't see anything about the Dog or Animal class, since both are empty. And the Visitor moves the doStuff out of the classes being visited, so it satisfies that requirement. – duffymo Dec 23 '10 at 12:51
  • ": AbstractAnimal" , it's the way I'm talking about. How do I do if there is more parameters in the "accept" method ? And I never said that you don't know the pattern, why would I said that, I'm just speaking about me and my colleagues. – remi bourgarel Dec 27 '10 at 08:39
  • I think there is a mistake in your implementation : you need to add a call to visit() in the Dog class (so you have to make accept virtual and then create an override), if you don't then you'll always use "void visit(Animal animal);" instead of "void visit(Dog dog); " when you have a Dog . Can you confirm ? – remi bourgarel Mar 23 '11 at 09:01
1

The reason is that the second call, although passing a Dog, is passing a reference to an animal. That reference can be any type of animal (you just happen to have passed a Dog) but .NET does not know that so it must call the version of the method that accepts a reference to an Animal rather than the more specific reference.

Colin Mackay
  • 18,736
  • 7
  • 61
  • 88
  • Ok, but why this would work if doStuff were a part of the animal and the dog class ? Still .net would reference an Animal but it will call the Dog's method. – remi bourgarel Dec 23 '10 at 08:23
  • I presume you mean that you would have a virtual method on Animal that was overriden in the Dog class? Then you have an inheritance relationship between the methods and the compiler can help out you. You have not defined (and cannot define) such a relationship in your service class – Colin Mackay Dec 23 '10 at 09:58
  • My remark was just a question about the logic behind all this : If I override a class method, .net will call the right one, but if I override a service class method .net won't that's why I'm kind of surprised. – remi bourgarel Dec 27 '10 at 08:41
  • But you are not overriding in the service class. That's the difference, you are just creating a new method with a very similar signature on the same class. Overriding uses inheritance to get do what it does. Your service class is not inheriting any behaviour to override. – Colin Mackay Jan 04 '11 at 23:52
1

Another thing which might seem a hack is to cast the passed parameter to dynamic. This would let you implement a 'dynamic visitor', so that for example you would write

foreach (dynamic a in animals)
  doStuff(a);

void doStuff(Cat c) { ... }
void doStuff(Dog d) { ... }

and so on. This lets you avoid the 'double dispatch' approach completely because you're using dynamic dispatch instead. Please be aware that this approach is much more computationally intensive and might be infeasible in some (e.g., iterative) scenarios.

Dmitri Nesteruk
  • 23,067
  • 22
  • 97
  • 166