Yosi Dahari
Yosi Dahari

Reputation: 6999

Polymorphism in generics - compile error when passing inherited object (Cannot implicitly convert type)

The following code does not compile:

protected override ITaskScheduleAlgorithm<CollectionTask, ICoordinationExecutionService<CollectionTask>> GetAlgorithm()
{
    return new SimpleTaskScheduleAlgorithm<CollectionTask, WorkerServiceConfiguration>();
}

The error is:

Cannot implicitly convert type...

The types that could not be converted:

WorkerServiceConfiguration → ICoordinationExecutionService

While the class WorkerServiceConfiguration inherits from ICoordinationExecutionService:

public class WorkerServiceConfiguration : AbstractServiceConfiguration<CollectionTask>
{
...
}

public abstract class AbstractServiceConfiguration<TTask> : ICoordinationExecutionService<TTask>
{
...
}

Any idea why it's happening and how it can be resolved?

Upvotes: 3

Views: 396

Answers (2)

Tim Copenhaver
Tim Copenhaver

Reputation: 3302

I think you have too many layers of generics and it's getting confusing. The actual answer is at the bottom, but first a digression to explain the problem...

You're correct that WorkerServiceConfiguration inherits from ICoordinationExecutionService. That's why this code does work:

protected ICoordinationExecutionService<CollectionTask> Test()
{
    return new WorkerServiceConfiguration();
}

But, you're not returning an ICoordinationService. You're returning an ITaskScheduleAlgorithm. Ignore the fact that the second one is an ICoordinationExecutionService, all you care about is this interface declaration. In fact, for the sake of understanding the problem, let's simplify it further:

public interface ITaskScheduleAlgorithm<TType>
{
}

public class SimpleTaskScheduleAlgorithm<TType> : ITaskScheduleAlgorithm<TType>
{
}

public ITaskScheduleAlgorithm<object> GetAlgorithm()
{
    return new SimpleTaskScheduleAlgorithm<string>();
}

If you try running this simplified example, you'll get the exact same error. You know that string derives from object, so you would expect with polymorphism that this should work. However, just because string derives from object does not mean SimpleTaskScheduleAlgorithm implements ITaskScheduleAlgorithm. It depends on exactly what generic methods are implemented. Think of the case of a List for example - even though string derives from object, you cannot add new objects to a List.

In order to make this work, you need to add the 'out' keyword to your interface declaration. For our simple case:

public interface ITaskScheduleAlgorithm { }

This indicates the interface is covariant (see covariance and contravariance in generics). Note that you can only make an interface covariant if it has no methods which accept an instance of the generic type as a parameter. So, the final answer for your problem is to add covariant support to your ITaskScheduleAlgorithm interface:

public interface ITaskScheduleAlgorithm<TType, out TType2>
{
}

Upvotes: 1

nikodz
nikodz

Reputation: 727

You should do like this

protected override ITaskScheduleAlgorithm<CollectionTask, ICoordinationExecutionService<CollectionTask>> GetAlgorithm()
{
    return new SimpleTaskScheduleAlgorithm<CollectionTask, ICoordinationExecutionService<CollectionTask>>();
}

Then you can use WorkerServiceConfiguration type objects

Upvotes: 1

Related Questions