views:

49

answers:

2

I've defined the following generic class

public class ManagedClass<T> where T : ManagedClass<T>
{
    static ManagedClass()
    {
        Manager = new ObjectManager<T>();
    }
    public static ObjectManager<T> Manager { get; protected set; }

    public ManagedClass()
    {
        Manager.Add( (T)this );
    }
}

The idea is that I can use it like so:

class Product : ManagedClass<Product> {}

Now I can do something to the 7th product created like so:

Product.Manager.GetById(7).DoSomething();

The problem comes in if i try to use a derived class:

class ExtendedProduct : Product {}

now ExtendedProduct.Manager has a list of 'Products', and if i want to use a new function that I have added to ExtendedProduct (DoSomethingElse), I have to cast the object I get back like so:

((ExtendedProduct)ExtendedProduct.Manager.GetById(7)).DoSomethingElse();

This is a bit ugly, and the whole point of using generics for this is to avoid casting. I suppose I could add a static constructor to the derived class to set Manager = new ObjectManager() and add a new Manager.addObject( this ) in the derived class constructor, but It seems like there should be some better way of doing this using generics. Any suggestions?

+1  A: 

The problem is that ExtendedProduct.Manager is the same thing as Product.Manager; the manager object can't act differently depending on where it's accessed from.

A couple of possibilities I can think of:

  1. Hide the typecast inside the GetById method by making it generic: Product.Manager.GetById<ExtendedProduct>(7).DoSomethingElse();

  2. Use one ObjectManager instance per subclass, connecting them privately if needed

Option 1 reminds me of NHibernate's ICriteria interface. It's effectively the same as a typecast, but a little harder to accidentally break.

Tim Robinson
+1  A: 

Really what you're running into is a weakness with Generics. Once your class has resolved what type it's using for generics, you're somewhat restricted in what you can do.

Normally, I'd say Dependency Injection would be a savior here, but since the problematic method is static, that muddies up the waters.

I'd say the best thing is to have the ObjectManager class do the work for you:

static public class ObjectManager<T>
{
    ... the code that already exists in ObjectManager ...

    static public U GetById<U>(long id)
    {
        object obj = GetById(id);
        if (obj is U)
            return (U)obj;
        return default(U);
    }
}

Then, in your code:

ExtendedProduct.Manager.GetById<ExtendedProduct>(7).DoSomethingElse();

It's not really tons more elegant than casting, but may be one of the only solutions using Generics.

Doug