views:

260

answers:

4

I have an abstract base class

class AbstractClass 
{
    Col<AbstractClass> parent

    public AbstractClass()
    {
        //do stuff
    }
}

I have two implementations

class A : AbstractClass 
{ 
    Col<A> parent

    public A(Col<A> parent)
        :base(parent)
    {
        this.parent = parent;
    }  

}

class B : AbstractClass 
{ 
    Col<B> parent

    public B(Col<B> parent)
        :base(parent)
    {
        this.parent = parent;
    }
}

I have a collection

class Col<T> : IList<T> where T : AbstractClass

Which is to be used in another class as Col<A> and Col<B>, let's call this class C.

class C
{
    List<A> a = new List<A>()
    List<B> b = new List<B>()
}

This would all work, except that I want types A and B to know about their parent collection. I thought having the following constructors in AbstractClass, A, B would be ok, but it seems generic constraints are only available for classes and not on methods. Essentially I would like the following constructors:

public AbstractClass(Col<T> where T : AbstractClass)
public A(Col<A>)
public B(Col<B>)

instances of A, B need to know what collection they're in, but I can't call the base constructor from the derived classes because they're different types.

Help!

+1  A: 

I want types A and B to know about their parent collection

You might be interested in this article. I wrote it with XML serialization in mind, but it can be used in a more general context

Thomas Levesque
+1  A: 

Use a custom Collection<T> type instead of a List<T>. They basically provide the same functionality (Collection<T> uses List<T> internally), and is intended precisely for this purposes.

You can then override InsertItem to set the parent of any item added to the collection.

public class MyCollection<T> : System.Collections.ObjectModel.Collection<T>
{
    protected override void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);
        item.Parent = this;
    }
}
Noldorin
so, do away with the constructors?
Matt Jacobsen
@Matt: I didn't include the constructors the purpose of conciseness. You'll want to add in the constructors that chain to the base ones though, of course.
Noldorin
I've edited my question to better explain my problem. I think we're on the right track here though
Matt Jacobsen
No, this won't work. I'd stil need something like protected Col<AbstractClass> parent in the Abstract class, to be overidden by protected Col<A> parent in A, and protected Col<B> parent in B.
Matt Jacobsen
@Matt: You could hide the `Col<AbstractClass>` property in the base class using the `new` keyword, or just leave it how it is and cast it. You can't override however, and this is something you'll have to live with.
Noldorin
Also, note that you can have generic type constraints on methods/constructors. e.g. `public void Foo<T>(T bar) where T : AbstractClass { ... }`
Noldorin
@Noldorin: Thanks. You're right: the problem is currently unsolveable with generics in c# 2 :)
Matt Jacobsen
A: 

As far as I understand it, this can't be done in C# 2.0. The functionality is already sitting in the CLR, but C# doesn't allow it. In C# 2.0 we have invariant generics. In C# 4.0 there will be support for variance.

Here are my references:

http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

http://code.msdn.microsoft.com/csharpfuture

http://channel9.msdn.com/pdc2008/TL16/

Am I right?

Matt Jacobsen
+3  A: 

I often use a pattern related to the C++ curiously recurring template pattern when I want to do something like this:

abstract class AbstractClass<TDerivedClass>
    where TDerivedClass : AbstractClass<TDerivedClass>
{
    Col<TDerivedClass> parent;

    public AbstractClass(Col<TDerivedClass> parent)
    {
        // do stuff
        this.parent = parent;
    }
}

class A : AbstractClass<A>
{ 
    public A(Col<A> parent)
        :base(parent) {}
}

class B : AbstractClass<B>
{ 
    public A(Col<B> parent)
        :base(parent) {}
}
Alexey Romanov