[.net] Visitor pattern not c# friendly (cast problems)

This topic is 4249 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Getting the hang of C#, but some of the ways it handles inheritance differently from C++ is driving me crazy. Take a look at this example,(obviouslu not real,
    class Node { }
class NewNode : Node {}

class Visitor
{
public void Accept(Node n)
{
Assert.IsTrue(false);
}

public void Accept(NewNode p)
{
Assert.IsTrue(true);
}
}

class Foo
{
public Foo()
{
NewNode fooBar = new NewNode();
Visit(fooBar);
}

public void Visit(Node n)
{
Visitor v = new Visitor();
v.Accept(n);
}
}


Unlike C++, which would pass, the C# version of this code fails. The reason (I am guessing) is that even though the n in Foo.Visit is of type NewNode, it gets cast to Node, and so Visitor.Accept(Node n) is called instead of Visitor.Accept(NewNode n) Running it through the debugger, c# obviously knows that the variable is of type NewNode, but it won't call the proper function. Is there a way to at least cast back to the original type in Foo::Visit so that it does call the proper method?

Share on other sites
It is being casted I believe because you passed it in as a Node. If you passed it in as a NewNode you could then call v.Accept(Node(n)); With C# you cannot go backwards down an inheritance tree so when you pass your NewNode in as a Node, it loses its NewNode functionality for that function unless you recast it.

Share on other sites
This is what I hate most about c#. It casts NewNode into Node for me. After all, the whole idea of polymorphism is that I don't need to know what kind of "node" I pass into this method. It knows it's own type. It's really quite wasteful IMHO to have to write an Accept method for every possible type of Node.

In my opinion, this is one case where C++ got it much better than C#. At least I figured there must be a way to cast it back to the original type. Urgh, it's frustrating because the entire visitor pattern breaks down in C# because of stuff like this.

Share on other sites
You do not have to do that, something does seem off to me. One way to get around the problems is to have an if to type check inside the Visit() function.

Share on other sites
Uhhh... I don't know what you're remembering, but C++ does it exactly the same way. Overloading is not polymorphism.

Fails assertion:

#include <cassert>class Node { };class NewNode : public Node {};class Visitor{public:        void Accept(Node* n)        {            assert(false);        }        void Accept(NewNode* p)        {            assert(true);        }};class Foo{public:        Foo()        {            NewNode* fooBar = new NewNode();            Visit(fooBar);        }        void Visit(Node* n)        {            Visitor* v = new Visitor();            v->Accept(n);        }};int main(){        Foo f;}

Share on other sites
Yikes! You're right. For some reason I remembered C++ as being able to distinguish the type and call the appropriate method rather than being cast, but running that code it seems to do the same thing.

I guess there isn't a way to make things elegant, and I'll have to handle each "node" type seperately.

My mistake, and thanks for poiting it out. I no longer have to be mad at C#, now I can be mad about both languages :)

Share on other sites
Actually, I don't think you should be angry at either... but, rather, at your recollection of the visitor pattern. Why is there a Visitor::Accept? Visitors visit. Nodes accept. And there shouldn't be any need for explicit downcasting. Example

EDIT: Most importantly, the only time that Visit is called, is as "Visit(this)". Since "this" has the derived class type, the appropriate overload will be called with no need for runtime polymorphism.

Share on other sites
Quote:
 Original post by AtaruFor some reason I remembered C++ as being able to distinguish the type and call the appropriate method rather than being cast, but running that code it seems to do the same thing.

Choosing an overload based on the runtime type of a parameter is called "multiple dispatch" or "multimethods" and if either C# or C++ had it, you wouldn't need the visitor pattern in the first place.

Share on other sites
You got the Visit and Accept methods in the wrong classes. Reverse those, and make the Visit and Accept methods virtual/overrides.
See http://www.dofactory.com/Patterns/PatternVisitor.aspx

Share on other sites
Quote:
 Original post by AtaruYikes! You're right. For some reason I remembered C++ as being able to distinguish the type and call the appropriate method rather than being cast, but running that code it seems to do the same thing.I guess there isn't a way to make things elegant, and I'll have to handle each "node" type seperately.My mistake, and thanks for poiting it out. I no longer have to be mad at C#, now I can be mad about both languages :)

Imagine it would be like you want...

Imagine you have a case where you NEED to call the function with the base class as parameter. It would be just impossible. The way it is now you could choose by casting. The way you want it you would be forced to one function.

Visit((Node)fooBar)