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);
}
}
[.net] Visitor pattern not c# friendly (cast problems)
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,
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?
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.
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.
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.
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.
Uhhh... I don't know what you're remembering, but C++ does it exactly the same way. Overloading is not polymorphism.
Fails assertion:
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;}
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 :)
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 :)
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.
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.
Quote:Original post by Ataru
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.
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.
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
See http://www.dofactory.com/Patterns/PatternVisitor.aspx
Quote:Original post by Ataru
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 :)
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.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement