2

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.

5
  • At first glance this looks like a variant of co- and contra-variance, though I might be wrong. Commented Jun 30, 2010 at 13:44
  • I think it's not solvable. In principle it could be solvable via covariance (in C# 4, not 3.5 anyway), but it would imply making a covariant class, which is not allowed anyway. Commented Jun 30, 2010 at 13:50
  • By the way, covariance can be used in .NET 3.5 as well. You just have have to use targeted compiler from .NET 4. Commented Jun 30, 2010 at 13:55
  • C# is not the same thing as .NET. C# 4.0 can be used with .NET 3.5. I don't know if variance requires CLR support or not. If it does, then .NET 4.0 would be required. Commented Jun 30, 2010 at 19:22
  • please don't repeat tags ("C#") in the title. That's what the tags are for. Commented Jun 30, 2010 at 19:22

3 Answers 3

3
class Derived2<T, U>: Base2<T, U>
        where T: IMyClass
        where U: IMyDerived1, IBase1<T>
    {
    } 
Sign up to request clarification or add additional context in comments.

2 Comments

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?
As Stefan noted, the constraints in Derived2 don't imply, that U is derived from IBase1<T>. But this is required by Base2.
1

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?

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.