KingKerosin
KingKerosin

Reputation: 3851

Abstract method to be overridden with concrete type

Maybe this is a dumb question. But, I don't get the point what I am missing.

Given the following class-definition

public abstract class AbstractBaseClass
{
    public abstract void Create(AnotherAbstractClass param1);
}

Wheras AnotherAbstractClass is defined

public abstract class AnotherAbstractClass
{
}

with a concrete implementation

public class AnotherConcreteImplementation : AnotherAbstractClass
{
}

I want to be able to have the override of the Create method to use a concrete type:

public class ConcreteImplementation : AbstractBaseClass
{
    public override void Create(AnotherConcreteImplementation param1) <-- There is no suitable method for override
    {
        // param1 is an instance of the concrete implementation
    }

    public override void Create(AnotherAbstractClass param1) <-- this is working but I'll have to cast on each implementation
    {
        // param1 is an instance of the abstract class and needs a cast
    }
}

Is this simply not possible or is there some way I'm not aware of? Maybe using generics?

Edit #1 (added more context)

I'm trying to achieve/enforce that in a concrete implementation there is only one parameter valid. Think of it like it's a database-layer. The Create method will create a new entry in the database. As of each table has different values, the create-parameter also has. The casting inside smells (in my opinion) as of it can be called with any concrete implementation of AnotherAbstractClass.

public class AddressTable : AbstractBaseClass
{
    public override void Create(AnotherAbstractClass param1)
    {
        // cast to concrete instance
        var casted = (ConcreteAddressCreate)param1;
    }
}

public class CityTable : AbstractBaseClass
{
    public override void Create(AnotherAbstractClass param1)
    {
        // cast to concrete instance
        var casted = (ConcreteCityCreate)param1;
    }
}

Having an instance of AddressTable I can call

addressIntance.Create(new ConcreteAddressCreate()); // would be okay

on the other hand I can call it

addressIntance.Create(new ConcreteCityCreate()); // would be okay but will fail at runtime with InvalidCastException

Edit #2 (additional info)

It should also be possible to extend the AbstractBaseClass class with more abstract methods later. So, for me it's more likely to have generic methods instead of an concrete class-implemenation with 200 generic parameters for each method to implement.

Upvotes: 2

Views: 1343

Answers (3)

CodeCaster
CodeCaster

Reputation: 151740

Yes, you can do that using generics:

public abstract class AbstractBaseClass<T>
    where T : AnotherAbstractClass
{
    public abstract void Create(T param1);
}

public class ConcreteImplementation : AbstractBaseClass<AnotherConcreteImplementation>
{
    public override void Create(AnotherConcreteImplementation param1)
    {
    }
}

Upvotes: 2

Rob Lyndon
Rob Lyndon

Reputation: 12691

Generics is indeed the way to do it.

public abstract class AbstractBaseClass<TDerivedClass> where TDerivedClass : AnotherAbstractClass
{
    public abstract void Create(TDerivedClass param1);
}

And then you can do:

public class ConcreteImplementation : AbstractBaseClass<AnotherConcreteImplementation>
{
    public override void Create(AnotherConcreteImplementation param1) // Works because TDerivedClass = AnotherConcreteImplementation 
    {
        ...
    }
}

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276596

It violates the Liskov Substitution Principle so it makes perfect sense you can't do this. Namely, you can't just "have" covariance like this for free:

AbstractBaseClass bcl = new ConcreteImplementation();
bcl.Create(new DifferentImplementationWithoutSecondAbstract());

The contract AbstractBaseClass defines makes Create have to work with any implementation of AbstractBaseClass passed in - if you give a constraint on what can be passed in you've violated the contract it defines.

Like you assumed - you can use generics:

// notice the recursive definition, we require the generic parameter
// to be a generic parameter of itself - allowing AbstractBaseClass
// to not be aware of its subclasses like in the other answers.
public abstract class AbstractBaseClass<T> where T : AbstractBaseClass<T>
{
    public abstract void Create(T param1);
}

public class Concrete : AbstractBaseClass<Concrete>
{
    public override void Create(Concrete param1)
    {
        Console.WriteLine("Hello!");
    }
}

Upvotes: 2

Related Questions