• Advertisement
Sign in to follow this  

[.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.

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
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.

Share this post


Link to post
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 this post


Link to post
Share on other sites
Guest Anonymous Poster
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.

Share this post


Link to post
Share on other sites
Visit((Node)fooBar)


If you want to call different functions depending on the 'real' type of fooBar, put the functionality in the Node class and override it in the NewNode class. You need to declare virtual and override though.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement