views:

139

answers:

4

I create base generic class with no fields with just one method

public class Base<T> where T:class
{
  public static T Create()
  {
     // create T somehow
  }
}

public class Derived1 : Base<Derived1>
{
}

public class Derived2 : Base<Derived2>
{
}


public class Program
{
    bool SomeFunction()
    {
     // Here I need reference to base class
     Base baseref; // error here 

     switch(somecondition)
     {
       case 1:
        baseref = Derived1.Create();
       break;

       case 2:
        baseref = Derived1.Create();
       break

     }

     // pass baseref somewhere     
    }
}

An obvious option would be converting base class to interface, but this is not possible because interface cannot contain static methods.

I think I need some intermediate base class. Please suggest

+5  A: 

You must remove the generic parameter from the Base class, you can move it to just the Create method:

public class Base 
{
    public static T Create<T>() where T : class
    {
        return Activator.CreateInstance<T>();
    }
}

public class Derived1 : Base
{
}

public class Derived2 : Base
{
}
Albin Sunnanbo
Looks good, but if there is solution without template methods?
Captain Comic
It depends, if you want your Create method to return the specific type T you need templates, but if you can return Base from the Create method you can send in an enum or string or something to define the type you want to create.
Albin Sunnanbo
+1 You got to the core of the issue first.
John K
Right now the factory method can create an instance of anything. To restrict it to derived types the author would need to specify `where T : Base` instead of `class`
John K
+1  A: 

How about creating an interface and having the abstract class implement the interface?

Monkieboy
+2  A: 
public abstract class Base
{
}

public class Base<T> : Base where T : class
{
    public static T Create()
    {
        // create T somehow
    }
}

public class Derived1 : Base<Derived1>    // also inherits non-generic Base type
{
}

public class Derived2 : Base<Derived2>    // also inherits non-generic Base type
{
}
LukeH
Cool!! Thanks, it just what I need! A simple solution, but I was just stuck
Captain Comic
In such scenarios I usually let the generic base class implement an interface. That's in my mind cleaner than this otherwise equivalent approach.
steinar
@steinar: I would probably go for an interface too in most circumstances, but the OP has hinted elsewhere in the question and comments that they need a class, so that's why I used an abstract base class in this example.
LukeH
+2  A: 

Preliminary Assessment

With this statement,

public class Derived1 : Base<Derived1> {

you're using Derived1 in two different ways according to the base class.

You're effectively telling the C# compiler that Derived1 both:

  1. inherits Base
  2. and Base uses instances of Derived1 through non-inheritance means.

This is not wrong (if that's what you really want), but it's unusual for most programming scenarios; you normally choose one or the other. However the benefit of your logic is: not only do you have an implicit instance of Derived1 through inheritance (same for any other derived class), but the base class can also handle other external instances of that same derived type through the type parameter <T>

One problem I see in the Base class is it turns into a kind of circular scenario when using the factory method as intended, because, to support all derived classes it would need to support something like class Base<T> where T:Base<T>. That's next to impossible to declare because you would have to say in a circular fashion: Base<Base<Base<!!!>>> baseref = null; where !!! represents an infinite number of the same.


One Solution...

One possible (and strong solution) is to move the Type parameter from the class to the factory Create method and restrict its usage to the Base class type like so:

using System;

public abstract class Base 
{
    public static T Create<T>() where T : Base
    {
        return Activator.CreateInstance<T>();
    }

}

Note: I have made the base class abstract which restricts instantiation to the derived types; however you can still use base class references (see switch statement usage below).

These derived classes still inherit from base.

public class Derived1 : Base
{
}

public class Derived2 : Base
{
}

Your factory method is restricted to create only instances of derived types. The logic has been swapped around so the derived type is given to the factory method instead of the factory method being called on it.

public class Program
{
    bool SomeFunction()
    {

    Base baseref = null;

     switch(DateTime.Now.Second)
     {
       case 1:
        baseref = Base.Create<Derived1>();  // OK
       break;

       case 2:
        baseref = Base.Create<Derived2>();  //OK
        break;

       case 60:
        baseref = Base.Create<string>(); //COMPILE ERR - good because string is not a derived class
        break;
     }

     // pass baseref somewhere     
    }
}
John K