Search code examples
c#genericsinheritancevisitor-pattern

Apply generic visitor to generic derived class of non-generic base class


Currently I have something like this:

public abstract class Base {...}
public class Derived<T> : Base {...}

class Visitor {
    public static void Visit<T>(Derived<T> d) {
        ...
    }
}

My question is, given a Base reference that I know is a Derived instance, how can I apply that Visit function to that object, using the correct generic instantiation? I understand that the answer will probably involve a type-checked dynamic downcast, to make sure that the object isn't some other type derived from base, which is all fine. I assume the answer involves reflection, which is also fine, though I'd prefer if there was a way to do it without reflection.

It's also ok if the answer involves an abstract method on Base and Derived; I do have enough control of the classes to add that. But at the end of the day, I need to call a generic function, correctly instantiated with the T of the Derived type.

Sorry if this is an easy question; I come from a C++ background, where my instinct would be to use a CRTP or something else like that, which isn't possible in C#.

EDIT:

Here's an example of what I need to be able to do:

Base GetSomeDerivedInstance() { ...; return new Derived<...>(); }
var b = GetSomeDerivedInstance();

// This is the line that needs to work, though it doesn't necessarily
// need to have this exact call signature. The only requirement is that
// the instantiated generic is invoked correctly.
Visitor.Visit(b);

Solution

  • In my opinion, answers involving double-dispatch and More Classes are going to be superior than using reflection to do what inheritance should do for you.

    Normally this means defining an 'accept' method on the visitable class, which simply calls the correct Visit method from the visitor.

    class Base
    {
        public virtual void Accept(Visitor visitor)
        {
            visitor.Visit(this); // This calls the Base overload.
        }
    }
    
    class Derived<T> : Base
    {
        public override void Accept(Visitor visitor)
        {
            visitor.Visit(this); // this calls the Derived<T> overload.
        }
    }
    
    public class Visitor
    {
        public void Visit(Base @base)
        {
            ...
        }
    
        public void Visit<T>(Derived<T> derived)
        {
            ...
        }
    }
    

    Then you can do what you mentioned in your question, with a small modification:

    Base b = createDerived();
    b.Accept(new Visitor());
    

    If your visit method is a static class that you can't change for whatever reason, you could always wrap this into a dummy instance visitor class which calls the right static method.