2

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

Captain Comic
  • 15,744
  • 43
  • 110
  • 148
  • 1
    What kind of error do you get? "base" (lower case) is a keyword in C#. – Marijn Oct 16 '10 at 14:00
  • 1
    And you do not seem to have a non-generic class "Base" specified. – Marijn Oct 16 '10 at 14:02
  • Marijn, ok lets replace base with var1 – Captain Comic Oct 16 '10 at 14:03
  • 1
    Instead of getting bogged down in language mechanisms and mechanics, can you please explain _what exactly_ you want to achieve. Perhaps this is not the best way to do so? – Oded Oct 16 '10 at 14:04
  • Oded, I think my code is self-explanatory. I need two things, provide static intance creator, that is based on generics because it must create T, use polymorphism to work with base class reference – Captain Comic Oct 16 '10 at 14:09
  • 1
    Your `SomeFunction()` method is outside of a class - it won't compile. What class is it supposed to be in? – John K Oct 16 '10 at 14:10
  • ....importantly, is SomeFunction inside or outside the inheritance hierarchy shown? That will help us know what mechanism to best use to solve your problem. (-1 until the question is better explained). I can make some good assumptions but if I have to then the question needs fixing. Thanks. – John K Oct 16 '10 at 14:17
  • @Captain - You aren't making any sense. You just replied to Oded that you need to use generic and polymorphism, but below you replied to @Albin that you want a solution without generics. I can definitely see why people don't understand what you are trying to do. – John Kraft Oct 16 '10 at 14:20
  • JohK, updated the code as you requested. I omitted some details because its not copypast, I just typed. – Captain Comic Oct 16 '10 at 14:22
  • John Kraft. Let me explain my point. My first task was to provide creator function what would be inherited and that would be able to return specific instance of derived class, this why I said to myself, man, you need generic class here. Then later on, (while I was keeping writing my code), I discovered that I need to use polymorphism because I needed to work with some kind of base class reference, and here I realized that there is not base class because generic class is not a type at all. Then I tried to convert it to an interface but I failed because of static member. – Captain Comic Oct 16 '10 at 14:27
  • @CaptainC: Thanks for the clarity. Upvote point returned. – John K Oct 16 '10 at 14:27
  • Thanks. A lesson for me - learn how to explain clearly what you want! – Captain Comic Oct 16 '10 at 14:35

4 Answers4

6

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
  • 46,430
  • 8
  • 69
  • 108
  • Looks good, but if there is solution without template methods? – Captain Comic Oct 16 '10 at 14:04
  • 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 Oct 16 '10 at 14:13
  • 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 Oct 16 '10 at 15:54
3

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
  • 28,441
  • 31
  • 139
  • 229
2
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
  • 263,068
  • 57
  • 365
  • 409
  • Cool!! Thanks, it just what I need! A simple solution, but I was just stuck – Captain Comic Oct 16 '10 at 14:17
  • 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 Oct 16 '10 at 14:34
  • @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 Oct 16 '10 at 17:23
1

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

Mr. Mr.
  • 4,257
  • 3
  • 27
  • 42