views:

117

answers:

3

I have type hierarchy defined like this:

interface IMyClass
{
}

interface IBase1<T>
{
}

interface IBase2<T>
{
}

interface IMyDerived1 : IBase1<IMyClass>
{
}

class Base1<T, U> : IBase1<T>
    where U : IBase2<T>
{
}

class Base2<T, U> : IBase2<T>
    where U : IBase1<T>
{
}

class Derived1<T, U> : Base1<T, U>, IMyDerived1
    where T : IMyClass
    where U : IBase2<T>
{
}

class Derived2<T, U> : Base2<T, U*>
    where T : IMyClass
    where U : IMyDerived1
{
}

but Visual Studio 2008 (.net 3.5 SP1) says that parameter U in parent specifier of Derived2 (marked with *) is not convertible to IBase1<T>. Is this solvable?

EDIT:

It indeed looks like generics overuse but allows Base1,2 and Derived1,2 to apply operations on supplied types without a casts. Something like this:

class MyClass : IMyClass
{}

class MySpecific1 : Derived1<MyClass, MySpecific2>
{
    // use inherited properties and methods of type MyClass here
    // use properties of MySpecific2 returning MyClass without casts
}

class MySpecific2 : Derived2<MyClass, MySpecific1>
{
    // use inherited properties and methods of type MyClass here
    // use properties of MySpecific1 returning MyClass without casts
}

Probably this can be solved more elegantly with variance in .net4 but I'm stuck with 3.5 for now.

A: 

The problem is, in Derived2, T is not IMyClass, it could be some other class implementing this interface. In Base1, it is specified to be exactly IMyClass. Types with different generic arguments are not compatible in C# 3.0.

For me, this looks a bit like generic overuse. But I can't see the context.

Stefan Steinegger
A: 

That hurt my head!

Having a look at it, the problem lies with this definition:

interface IMyDerived1 : IBase1<IMyClass>
{
}

You've specialised that generic implementation, and then attempted to use with generic arguments later on:

class Derived2<T, U> : Base2<T, U>
    where T : IMyClass
    where U : IMyDerived1
{
}

Which is invalid. Not sure if this is correct, but can you make either this change:

interface IMyDerived1<T> : IBase1<T>
{
}

class Derived2<T, U> : Base2<T, U>
    where T : IMyClass
    where U : IMyDerived1<T>
{
}

That's a complicated hierarchy you're designing there, what will be its use?

Matthew Abbott
+2  A: 
class Derived2<T, U>: Base2<T, U>
        where T: IMyClass
        where U: IMyDerived1, IBase1<T>
    {
    } 
Henrik
@Henrik Great solution! I didn't thought of something so simple. Can you explain a little bit? It seems to me that compiler do not consider other constraints while evaluating expression in 'where U'. Is that right?
Bazurbat
As Stefan noted, the constraints in Derived2 don't imply, that U is derived from IBase1<T>. But this is required by Base2.
Henrik