views:

556

answers:

2

Hi!

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!

+1  A: 

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.

Meinersbur
For my specific problem, in the end, I chose this solution.
AlexH
A: 

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?

DonkeyMaster
Thanks for replying. I think I understand why I can't do this type of conversion in C#. Here, I am basically interested in solutions on how to solve the reference problem: Make the inner generic collection store a reference to the outer generic collection. In my code, both collections mostly act as base classes themselves. I'd then have specific implementations of Inner<>: DerivedInner : Inner<WhateverInnerItem>and use the Outer<> as in: Outer<DerivedInner, WhateverInnerItem>
AlexH