Bug in C# Compiler? (generics)

G

Guest

Hello,

Can anyone tell me why this won't compile. I'm getting the error:
"The type 'GenericsTest.GenericType3' must be convertible to
'GenericsTest.GenericType2<GenericsTest.IInterface2>' in order to use it as
parameter 'T' in the generic type or method 'GenericsTest.BaseCollection<T>"

However, GenericType3 clearly inherits from GenericType2<IInterface2>

************ Code *************
#region Interface Hierarchy
public interface IInterface1
{}

public interface IInterface2 : IInterface1
{}

public class InterfaceType : IInterface2
{}
#endregion Interface Hierarchy

#region Generic Hierarchy
public abstract class GenericType1<T1, T2>
{}

public class GenericType2<T> : GenericType1<long, T>
where T : IInterface2
{}

public class GenericType3 : GenericType2<InterfaceType>
{ }
#endregion Generic Hierarcy


#region Collection Hierarchy
public abstract class BaseCollection<T>
where T : GenericType2<IInterface2>
{}

public class Collection : BaseCollection<GenericType3>
{ }
#endregion Collection Hierarchy
************ End Code *************

Thanks in advance for any help you can provide.

Mike
 
B

Barry Kelly

AnotherMike said:
Can anyone tell me why this won't compile.
[...]
However, GenericType3 clearly inherits from GenericType2<IInterface2>
[...]

No it doesn't. It inherits from GenericType2<InterfaceType>, not
GenericType2<IInterface2>.

Given:

* a generic class G<T>
* a class C descended from
* a class B

So:

class G<T> { }
class B { }
class C : B { }

Now, there is no inheritance relationship between G<B> and G<C>. That is,
even though C is a subclass of B, G<C> is *not* a subclass of G<B>.

-- Barry
 
C

Chris Chilvers

Hello,

Can anyone tell me why this won't compile. I'm getting the error:
"The type 'GenericsTest.GenericType3' must be convertible to
'GenericsTest.GenericType2<GenericsTest.IInterface2>' in order to use it as
parameter 'T' in the generic type or method 'GenericsTest.BaseCollection<T>"

However, GenericType3 clearly inherits from GenericType2<IInterface2>

What this code is infact trying to do is an assignment like this:

public class A
{
public void Foo()
{
B<String> item1 = new B<String>();
B<Object> item2 = item1;

item1.Add("abc");

//if the above assignment of item1 to item2 was allowed then according to item2
//this statement would be allowed, whilst it should not be allowed, as item1 must
//contain strings and anything that inherits from string.
item2.Add(new Object());
}
}

public class B<T>
{
public void Add(T item) { }
}


As shown an assignment of B<Object> item = new B<String> is invalid dispite the fact that string inherits from object.
In your code it was effectivly like trying to use an assignment reading:

GenericType2<IInterface2> item = new GenericType2<InterfaceType>

What you in fact want is to make the type GenericType2<IInterface2> in your BaseCollection generic, something like:

public class BaseCollection<T> where T : GenericType2<U> where U : IInterface2

unfortunatly this is not valid, and nether is:

public class BaseCollection<T<U>> where T : GenericType2<U> where U : IInterface2

This only leaves this:

public abstract class BaseCollection<T, U>
where T : GenericType2<U>
where U : IInterface2
{ }

but now using BaseCollection is messy:

public class Collection : BaseCollection<GenericType3, InterfaceType>
{ }


Note:
Is there a way to get nested generics to work?
As in, is their any way to express this:
public class BaseCollection<T<U>> where T : GenericType2<U> where U : IInterface2

or have the compiler infer that U exists in some way? By infer I mean in the same mannor it can work out that Push is
infact Push<int> without having to explictly tell it.

Stack<int> s = new Stack<int>;
Push(s, 1, 2 ,3) //didn't have to tell it that Push was <int> as s is Stack<int>

void Push<T>(Stack<T>, params T items);
 
G

Guest

Thank you Barry and Chris for your replies. You have been most helpful.
While you have answered my question about the possibility of what I was
trying to accomplish, it is still a mystery to me WHY this is the case. Why
can you not

GenericType2<IInterface2> item = new GenericType2<InterfaceType>;

when InterfaceType inherits from IInterface 2. I can understand why the
opposite would not work:

GenericType2<InterfaceType> item = new GenericType2<IInterface2>;

What is preventing the compiler from doing the conversion/cast?

Mike
 
J

Jon Skeet [C# MVP]

AnotherMike said:
Thank you Barry and Chris for your replies. You have been most helpful.
While you have answered my question about the possibility of what I was
trying to accomplish, it is still a mystery to me WHY this is the case. Why
can you not

GenericType2<IInterface2> item = new GenericType2<InterfaceType>;

when InterfaceType inherits from IInterface 2. I can understand why the
opposite would not work:

GenericType2<InterfaceType> item = new GenericType2<IInterface2>;

What is preventing the compiler from doing the conversion/cast?

C# doesn't support type parameter covariance/contravariance. The CLR
does, but the framework doesn't use it either.

The easiest example of why it doesn't work is using List. Consider the
following example, where Bar1 and Bar2 implement IFoo:

List<IFoo> list = new List<Bar1>();
list.Add (new Bar2());

See the problem? You end up trying to add a Bar2 reference to a list of
Bar1 references, even though Bar2 can't be converted to Bar1.

See http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx for
more information.
 
C

Chris Chilvers

But does anyone know of a simpler way to get the equivlent of this (which is invalid):

public class BaseCollection<T<U>>
where T : GenericType2<U>
where U : IInterface2

The best I could come up with was:

public abstract class BaseCollection<T, U>
where T : GenericType2<U>
where U : IInterface2

But this is now rather messy to work with and use.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top