6

OK, that title is a little unclear, but I can't think of a better way of putting it, other than explaining it...

Say I have a class Animal, with a static, generic method:

public static T Create<T>() where T : Animal {
  // stuff to create, initialize and return an animal of type T
}

And I have subclasses Dog, Cat, Hamster etc. In order to get a Dog, I can write:

Dog d = Animal.Create<Dog>();

or

Dog d = Dog.Create<Dog>();

which is really the same thing. But it seems kinda silly to have to write Dog so many times, since I'm already invoking the static method through the Dog subclass.

Can you think of any clever way of writing a Create() method in the base class so that I could invoke

Dog d = Dog.Create();
Cat c = Cat.Create();
Hamster h = Hamster.Create();

without writing a Create() method in each of the subclasses?

Shaul Behr
  • 36,951
  • 69
  • 249
  • 387

3 Answers3

10

You can make the Animal class generic.

class Animal<T> where T : Animal<T>
{
    public static T Create()
    {
        // Don't know what you'll be able to do here
    }
}

class Dog : Animal<Dog>
{

}

But how the Animal class knows how to create instances of derived types?

Romain Verdier
  • 12,833
  • 7
  • 57
  • 77
  • 2
    You'll probably want to restrict `T` to Animals: `class Animal where T : Animal` – dtb Sep 09 '09 at 15:29
  • The code inside Create() will call virtual/abstract methods, that's how it'll create instances of derived types. – Shaul Behr Sep 09 '09 at 16:04
  • 1
    Actually, I have to comment again, because this idea of having Animal forcing T to be of type Animal is just sheer genius. At first glance it looks like infinite recursion, but it's not; it's a really elegant piece of logic that has just made my life a HECKUVA lot easier. So thanks again - I wish I could vote you up more! :) – Shaul Behr Sep 10 '09 at 13:45
  • I'm using this kind of constraint for my presenters, for exemple: interface IPresenter : IPresenter where TView : IView where TPresenter : IPresenter – Romain Verdier Sep 10 '09 at 14:42
2

I would make the Animal class abstract with a static Create method; it's effectively a starting point for a factory. In fact, it looks like you're undoing a factory class.

If you add an abstract Initialize method to the Animal class, the Create method becomes:

public static T Create<T>() where T : Animal {
  T animal = new T();   //may need a "new" in the declaration
  animal.Initialize();  //or Create or whatever or you put this logic
                        //   in the constructor and don't call this at all.
  return animal;
}
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
1

In addition to the other answers on ways around it, you can see using reflection that Create will always still be part of Animal, not the derived class.

Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142