drty
drty

Reputation: 210

Complex inheritance with generics

Let's say I have a set of classes/interfaces:

class ObjectData { }
class UnitData : ObjectData { }
class Component1Data : UnitData { }
class Component2Data : UnitData { }

interface IObject { }
interface IUnit : IObject { }
interface IComponent1 : IUnit { }
interface IComponent2 : IUnit { }

abstract class Object<D, O, I>
    where D : ObjectData
    where O : Object<D, O, I>, I, new()
    where I : IObject
{ }

The point of primary interest here is an Object class which is a base generic class in some hierarchy. Type-param "O" is a way to specify type of an actual class which is derived from the Object. Thus, something like this can be declared and compiled w/o problems:

class Unit : Object<UnitData, Unit, IUnit>, IUnit { }

But what i want to do, is to define another generic "2nd-level" class derived from Object that should also behave as a base class for a couple of similar "3rd-level" entities. And it have to be non-abstract because it is also some sort of an entity. So i need to define something like this:

class Unit<D, I> : Object<D, Unit<D, I>, I>, IUnit
    where D : UnitData
    where I : IUnit
{ }

class Component1 : Unit<Component1Data, IComponent1>, IComponent1 { }
class Component2 : Unit<Component2Data, IComponent2>, IComponent2 { }

And it produces following compilation error:

error CS0311: The type 'Unit<D, I>' cannot be used as type parameter 'O' in the generic type or method 'Object<D, O, I>'. There is no implicit reference conversion from 'Unit<D, I>' to 'I'.

The question is why? In my vision if Unit<D, I> is implementing IUnit, and param "I" is specified as where I : IUnit, then all should be fine. That's how i see it. What i don't see?

Upvotes: 1

Views: 1187

Answers (2)

Meirion Hughes
Meirion Hughes

Reputation: 26408

Simplify the problem;

interface IObject { }
interface IUnit : IObject { }
interface IFoo : IUnit { }

abstract class Object<O, I>
    where O : Object<O, I>, I, new()
    where I : IObject
{}

class Unit : Object<Unit, IUnit>, IUnit
{
}

This is happy and compiles. I've replaced I here with IUnit. Now change to something more derived:

class Unit : Object<Unit, IFoo>, IUnit

and you get the error:

The type 'Unit' cannot be used as type parameter 'O' in the generic type or method 'Object'. There is no implicit reference conversion from 'Unit' to 'IFoo'.

So... Unit, which derives from IUnit cannot be converted to IFoo even though both implement IUnit... because Unit doesn't derive from IFoo... which is a condition on Object:

where O : Object<O, I>, I`

which requires you to do something you're not allowed to do :

class Unit<I> : Object<Unit<I>, I>, I

Upvotes: 1

Dennis
Dennis

Reputation: 37770

Like others have commented, your generics are too complex. At least, it seems to me, that there's no need for I type parameter, because your Objects will implement corresponding interface.

So, the code could be simplified like this:

abstract class Object<D, O> : IObject
    where D : ObjectData
    where O : Object<D, O>
{
}

class Unit<D> : Object<D, Unit<D>>, IUnit
    where D : UnitData
{
}

Without full understanding, how O will be used inside of Objects hierarchy, it is hard to say, is it possible to throw away O type parameter.

You've mentioned a static factory method - this is definitely not a reason to bring such complexity. But, of course, you know more about use cases.

Upvotes: 1

Related Questions