Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Generic Class Instance from Static Method in a Derived Class

I have a class in C# with a template and static method similar to

class BClass<T>
{
  public static BClass<T> Create()
  {
    return new BClass<T>();
  }
 }

From this I derive a class and specify a template parameter to the base class

class DClass : BClass<int> { }

A problem occurs when I try to use the static method to create an instance of D

class Program
{
  static void Main(string[] args)
  {
    DClass d = DClass.Create();
  }
}

Gives a compiler error "Cannot implicitly convert type 'Test.BClass<int> ' to 'Test.DClass'."

Adding the below cast leads to a runtime casting exception.

DClass d = (DClass)DClass.Create();

Is there any succint way to allow the static method to create instances of the derived class? Ideally I would like the equivalent of a c++ typedef and I don't want the below syntax (which does work).

BClass<int> d = DClass.Create();
like image 519
Jack Nock Avatar asked May 27 '10 11:05

Jack Nock


2 Answers

It seems that what you want is for DClass to be an alias for BClass<int>. But that's not what you have here. What you have is that DClass derives from BClass<int>. Thus calling DClass.Create(); creates a BClass<int>, which is not a DClass (it's the other way around).

This might make it clearer. Suppose you had this hierarchy:

class Rectangle {
    static Rectangle Create() {
        return new Rectangle();
    }
}

class Square : Rectangle { }

// code elsewhere
var shape = Square.Create(); // you might want this to return a square,
                             // but it's just going to return a rectangle

One option to get something like the functionality you want might be to define your Create method like this:

static TClass Create<TClass>() where TClass : BClass<T>, new() {
    return new TClass();
}

// code elsewhere
var shape = DClass.Create<DClass>();

That's a bit messy, though (and also requires that you write a parameterless constructor for DClass). If you're dead set on using a Create method for the instantiation of your objects, consider writing a factory class for it.

like image 90
Dan Tao Avatar answered Nov 15 '22 14:11

Dan Tao


Yes it is possible, by having a type reference to the type itself. Note that in the .NET world we are talking of generics, not templates, which do have important differences in the way they work.

class BClass<T, TSelf> where TSelf: BClass<T, TSelf>, new() {
    public static TSelf Create() {
        return new TSelf();
    }
}

class DClass: BClass<int, DClass> {}

class Program {
    static void Main(string[] args) {
        DClass d = DClass.Create();
    }
}
like image 30
Lucero Avatar answered Nov 15 '22 14:11

Lucero