Reputation: 1688
While implementing a design using nested generic collections, I stumbled across those limitations apparently caused by C#'s invariant Generics:
Cannot convert from 'Collection<subtype of T> to 'Collection<T>'
That means, the following will not work, apparently due to the invariance of Generics:
class Outer<TInner, TInnerItem> where TInner : Inner<TInnerItem>
{
public void Add(TInner item)
{
item.Outer = this; // ERROR:
// Cannot implicitly convert from Outer<TInner, TInnerItem>
// to Outer<Inner<TInnerItem>, TInnerItem>
}
}
class Inner<TInnerItem> : ICollection<TInnerItem>
{
Outer<Inner<TInnerItem>, TInnerItem> _outer;
public Outer<Inner<TInnerItem>, TInnerItem> Outer
{
set { _outer = value; }
}
}
(In the actual code, both Inner<>
and Outer<>
implement ICollection<>
.)
I need the Inner<>
objects to have a reference to its container collection in order to access some of its data.
How would you implement these nested collections, preferably using a generic approach as outlined above? How would you set the reference to the container collection in the Inner<>
class?
Cheers!
Upvotes: 3
Views: 2744
Reputation: 1342
The language can't let you convert from Collection<subtype of T> to Collection<T>
Let me explain why.
Imagine you have a Collection<subT> and cast it to Collection<T>. That's ok, since subT inherits from T.
When you retrieve an object from collection_of_T
, you're assured that it's ok.
Later, add a T
object to collection_of_T
. Now you have, in collection_of_subT
an object which is not a subT
. Whoops.
In order to achieve what you want, you must create a new Collection alltogether.
Collection<T> collection_of_T = new Collection<T>(collection_of_subT);
Which probably is not good for you. Do you really need the TInner
generic argument in the Outer
class? Would it be possible to replace with Inner<TInnerItem>
in the rest of the code instead?
Upvotes: 0
Reputation: 8071
Introducing a (possibly abstract) base class which is not dependant on TInner may help you:
abstract class OuterBase<TInnerItem>
{
}
class Outer<TInner, TInnerItem> : OuterBase<TInnerItem> where TInner : Inner<TInnerItem>
{
public void Add(TInner item)
{
item.Outer = this; // Compiles
}
}
class Inner<TInnerItem> : ICollection<TInnerItem>
{
OuterBase<TInnerItem> _outer;
public OuterBase<TInnerItem> Outer
{
set { _outer = value; }
}
}
Or wait for C# 4.0, which introduces co/contra-variant generic interfaces.
Upvotes: 2