简体   繁体   中英

Implementing abstract/interface methods containing abstract/interface-type parameters

I have two interfaces:

public interface IController
{
    void doSomething(IEntity thing);
}

public interface IEntity { ... }

One example implementation would be:

public class ControllerImplA : IController
{
    public void doSomething(IEntity entity)
    {
        EntityImplA entityForA = (EntityImplA)entity;
        ...
    }
}

public class EntityImplA : IEntity { ... }

The entity parameter of ControllerImplA.doSomething() will always be EntityImplA . Likewise, the entity parameter of ControllerImplB.doSomething() will always be EntityImplB , and so on so forth for other implementations.

Is there a way to avoid using the downcasting in my current code? In other words, I want to do something like this:

public class ControllerImplA : IController
{
    public void doSomething(EntityImplA entity) { ... }
}

without modifying the interfaces? What about if abstract parent classes are used instead of interfaces?

You want to use generics. Modifying the interfaces is the only way to make this clean:

public interface IController<T> where T : IEntity
{
    void doSomething(T thing);
}

Then:

public class ControllerImplA : IController<EntityImplA>
{
    public void doSomething(EntityImplA entity)
    {
        ...
    }
}

However, if you really can't change the interface, eg it's provided by a third party library, then the workaround by @dasblinkenlight is about as clean as you're going to get.

If you have no control over the interface, implement it in a generic abstract class with a type parameter specifying the entity type, like this:

public interface IController {
    void DoSomething(IEntity thing);
}

public interface IEntity { ... }

abstract class AbstractController<TEntity> : IController where TEntity : IEntity {
    public void DoSomething(IEntity e) {
        // Forward the call to an abstract method with more specific type
        DoSomethingImpl((TEntity)e);
    }
    // Subclasses need to implement this method instead of the interface method:
    protected abstract void DoSomethingImpl(TEntity e);
}

Now your implementations can derive from AbstractController with the specific subclass, like this:

public class ControllerImplA : AbstractController<EntityImplA> {
    public void DoSomethingImpl(EntityImplA entity) { ... }
}

The cast is still there, but now it is shared among all implementations.

This is the best use-case for generics. To do so add a generic constraint to your interface as Baldrick already mentioned.

If - for whatever strange reason - you don´t want to change the interface, you are stuck on a really ugly solution that hides the casting from your API:

public class ControllerImplA : IController
{
    public void doSomething(IEntity entity)
    {
        this.doSomething((EntityImplA) entity);
    }

    public void doSomething(EntityImplA entity)
    {
        ...
    }
}

This way you can use both methods, the one from the interface which redirects to the other one, and the more derived one defined on your class but not on the interface.

var myIEntity = ...
var myEntity = new EntityImplA();
myControler.doSomething(myIEntity); // calls the interface-method
myControler.doSomething(myEntity);  // direclty calls the class-method

You could also explicitely implement your interface, so that when accessing the method from the interface you see only the more generic method while on the specific class you see only the more concrete implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM