Question

I always thought C# resolves method calls dynamically during runtime by looking at the runtime type of the method call receiver (i.e. the object before the dot).

However the following code sample works differently. If I use GenericSpaceShip in the code it returns "Generic"; if I use SpaceShip it returns "Specific". Note that the runtime type in both cases is SpaceShip.

So my question is: How does C# resolve the Visit method call and why does it look at the compile time rather than the runtime type in this situation?

Note that the two Visit methods have different parameters. As Patko points out, this means I cannot use virtual/override here.

class GenericSpaceShip
{
    public void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip
{
    public void Visit(Planet planet)
    {
        Console.WriteLine("Specific");
    }
}

class GenericPlanet { }

class Planet : GenericPlanet { }

class Starter
{
    static void Main(string[] args)
    {
        // SpaceShip ship = new SpaceShip();
        GenericSpaceShip ship = new SpaceShip();
        Planet planet = new Planet();

        ship.Visit(planet); // => Generic
    }
}
Was it helpful?

Solution

If you want to have really dynamic resolution, then you have to use dynamic keyword, like so:

static void Main(string[] args)
{
    dynamic ship = new SpaceShip();
    Planet planet = new Planet();

    ship.Visit(planet); // => Specific

    // also
    GenericPlanet genericPlanet = new GenericPlanet();
    ship.Visit(planet); // Generic
}

In this case behaviour will be something like you have described - type of the parameter matters. But what you most likely want is to have a method override, like so:

class GenericSpaceShip
{
    public virtual void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip
{
    public override void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Specific");
    }
}

In this case the Specific method will be called if you have an instance of SpaceShip and Generic method for instance of GenericSpaceShip, regardless of planet type. In this case type of the ship matters:

static void Main(string[] args)
{
    SpaceShip ship = new SpaceShip();
    Planet planet = new Planet();

    ship.Visit(planet); // => Specific

    // also
    GenericPlanet genericPlanet = new GenericPlanet();
    ship.Visit(planet); // Specific       
}

You would always get Generic if GenericSpaceShip was used instead.

OTHER TIPS

C# has two ways of doing it:

  • Resolve at run time (overriding) - this works for abstract and virtual methods, and for methods implementing an interface. Among other things, this requires that method return types and parameters are the same.
  • Resolve at compile time (hiding) - this works for methods that have the same name and arguments, but are not virtual.

This gives you maximum control over the method resolution process, but it also requires that you tell the compiler of your decision.

In your code the method in the derived class hides the method in the base class. Here is how to modify your code to make it an override:

class GenericSpaceShip {
    // Mark the base method virtual
    public virtual void Visit(GenericPlanet planet) {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip {
    // Mark the overriding method as such.
    // Also note that you cannot change argument types when you override:
    public override void Visit(GenericPlanet planet) {
        Console.WriteLine("Specific");
    }
}

Note the two things that need to happen:

  • The signature of the overriding method must be the same as that of the base method (hence, both of them take GenericPlanet; you can cast it if necessary).
  • You need to use keywords virtual and override to tell the compiler that the two methods are related.

In C# you have to declare your methods explicitely as virtual/abstract and override if you want to override them in a derived class.

So it should read

class GenericSpaceShip
{
    public virtual void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip
{
    public override void Visit(Planet planet)
    {
        Console.WriteLine("Specific");
    }
}

virtual and override are the keywords you're looking for.

class GenericSpaceShip
{
    public virtual void Visit(GenericPlanet planet)
    {
        Console.WriteLine("Generic");
    }
}

class SpaceShip : GenericSpaceShip
{
    public override void Visit(Planet planet)
    {
        Console.WriteLine("Specific");
    }
}

You are using inheritance for method overloading, which means you are just keeping two methods with different signatures in the base class(SpaceShip) and derived class(GenericSpaceShip).

Indirectly derived class objects will always have these two methods with a different signature and, this is going to check at compile time. You are not overriding any method having the same signature and return type, so there will not be any run-time or dynamic check.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top