generic methods: how to force using the most specialized method available

326 views Asked by At

I defined a generic method Use<T> in an interface IInterface. I tried to make an implementation of that interface where the concrete implementation of the Use<T> method depends on the actual type T, and I want to always call the most specialized method. But it does not work:

    interface IInterface { void Use<T>(T other) where T : IInterface; }
    interface IChildInterface : IInterface { }
    class ImplementsIInterface : IInterface
    {
        public void Use<T>(T other) where T : IInterface
        {
            Debug.WriteLine("ImplementsInterface.Use(IInterface)");
        }
    }
    class ImplementsChildInterface : IChildInterface
    {
        public void Use<T>(IChildInterface other) where T : IInterface
        { // idea: if other is IChildInterface, use this method
            Debug.WriteLine("ImplementsChildInterface.Use(IChildInterface)");
        }

        public void Use<T>(T other) where T : IInterface
        { // idea: if above method is not applicable, use this method
            Debug.WriteLine("ImplementsChildInterface.Use(IInterface)");
        }
    }

Here is my main method:

    public static void Main()
    {
        IChildInterface childinterf = new ImplementsChildInterface();

        childinterf.Use(new ImplementsChildInterface()); // outputs "ImplementsChildInterface.Use(IInterface)"
                                                         // but should output "ImplementsChildInterface.Use(IChildInterface)"

        childinterf.Use(new ImplementsIInterface());     // outputs "ImplementsChildInterface.Use(IInterface)"
    }

The method that takes an IChildInterface argument is never called, although it should.

Is there a way to make this work? Or is my approach fundamentally wrong?

Note that it is a necessity that IInterface only has one method definition. I might enlarge the interface hierarchy at any time (and thus increase the number of implementations I could provide in an implementing class), but this should not lead to needing to add more method definitions in IInterface. Otherwise, the whole point of using interfaces (i.e. to be flexible) would be missed.


The answers I got so far all involve the need to cast. This is also something I don't want to do, since it makes the whole setup useless. Let me explain the broader picture of what I try to achieve:

Let's imagine we created some instance of an IInterface (like so: IInterface foo = new ImplementsChildInterface();). It will behave in a certain way, but it will always behave in the same way - no matter if we see it as an IInterface, an IChildInterface or an ImplementsChildInterface. Because, if we call some method on it, the compiler (or runtime? i don't know) will check what type it REALLY is and run the method defined in that type.

Now imagine we have two instances i1 and i2 of IInterface. They again are, under the hood, concrete implementations of IInterface, so they have a concrete behaviour, no matter through which glasses we seem them.

So when I run i1.Use(i2), the compiler (or runtime?) should be able to find out what i1 and i2 REALLY are, and run the corresponding method. Like so:

  1. Which type does i1 have? ImplementsChildInterface, ok, then I'll look at the methods there.
  2. Which type does i2 have? ImplementsIInterface, ok, then let's see if there exists a method Use(ImplementsIInterface ...). There is none, but maybe there is a fallback? ImplementsIInterface is a IInterface, so let's see if there exists a method Use(IInterface ...). Yes, it exists, so let's call it!
1

There are 1 answers

2
MakePeaceGreatAgain On

Neither IInterface nor IChildInterface have a member Use<T>(IChildInterface other) defined, but only Use<T>(T other).

Your class ImplementsChildInterface on the other side has a method Use<T>(IChildInterface other). As you declaring childInterf as a reference of type IChildInterface you can´t access that member, but unly those defined in the interface. So you should cast to the actual class in order to access the method accepting an instance of IChildInterface. But even then the generic implementation is used. So you should also cast your parameter to IchildInterface:

ImplementsChildInterfacechildinterf = new ImplementsChildInterface();
childinterf.Use(((IChildInterface)new ImplementsChildInterface()); 
childinterf.Use(new ImplementsIInterface());

Furthermore as you don´t use the generic type-parameter within your more specialized method, you can also omit it:

class ImplementsChildInterface : IChildInterface
{
    public void Use(IChildInterface other)
    { // idea: if other is IChildInterface, use this method
        Debug.WriteLine("ImplementsChildInterface.Use(IChildInterface)");
    }

    public void Use<T>(T other) where T : IInterface
    { // idea: if above method is not applicable, use this method
        Debug.WriteLine("ImplementsChildInterface.Use(IInterface)");
    }
}

Alternativly you may also add a method into your IChildInterface:

void Use<T>(IChildInterface other) where T : IChildInterface;

Now you can use

IChildInterface childinterf = new ImplementsChildInterface();
childinterf.Use<IChildInterface>(new ImplementsChildInterface()); // outputs "ImplementsChildInterface.Use(IInterface)"

which will print the desired output.