Reputation: 210
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
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
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 Object
s 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 Object
s 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