90

I understand that static method inheritance is not supported in C#. I have also read a number of discussions (including here) in which developers claim a need for this functionality, to which the typical response is "if you need static member inheritance, there's a flaw in your design".

OK, given that OOP doesn't want me to even think about static inheritance, I must conclude that my apparent need for it points to an error in my design. But, I'm stuck. I would really appreciate some help resolving this. Here's the challenge ...

I want to create an abstract base class (let's call it a Fruit) that encapsulates some complex initialization code. This code cannot be placed in the constructor, since some of it will rely on virtual method calls.

Fruit will be inherited by other concrete classes (Apple, Orange), each of which must expose a standard factory method CreateInstance() to create and initialize an instance.

If static member inheritance were feasible, I would place the factory method in the base class and use a virtual method call to the derived class to obtain the type from which a concrete instance must be initialized. The client code would simple invoke Apple.CreateInstance() to obtain a fully initialized Apple instance.

But clearly this is not possible, so can someone please explain how my design needs to change to accommodate the same functionality.

yoozer8
  • 7,361
  • 7
  • 58
  • 93
Tim Coulter
  • 8,705
  • 11
  • 64
  • 95
  • 2
    I'd like to point out that the statement "This code cannot be placed in the constructor, since some of it will rely on virtual method calls" is not correct. You CAN call virtual methods inside a constructor. (You'll have to design your calls carefully, because you can have a partially initialized class when the virtual methods are called, but it is supported.) See Ravadre's answer for more details. – Ruben Sep 04 '09 at 17:01

8 Answers8

64

One idea:

public abstract class Fruit<T>
    where T : Fruit<T>, new()
{
    public static T CreateInstance()
    {
        T newFruit = new T();
        newFruit.Initialize();  // Calls Apple.Initialize
        return newFruit;
    }

    protected abstract void Initialize();
}

public class Apple : Fruit<Apple>
{
    protected override void Initialize() { ... }
}

And call like so:

Apple myAppleVar = Fruit<Apple>.CreateInstance();

No extra factory classes needed.

Matt Hamsmith
  • 3,955
  • 1
  • 27
  • 42
  • 10
    IMHO, it is better to move the generic type to the CreateInstance method instead of putting it on the class level. (Like i did in my answer). – Frederik Gheysels Sep 05 '09 at 01:18
  • 1
    Your preference is noted. A brief explanation of your preference would be more appreciated. – Matt Hamsmith Sep 05 '09 at 03:28
  • 10
    By doing so, you do not 'pollute' the rest of the class; the type parameter is only necessary in the Create method (in this example at least). Next to that, I think that: Fruit.Create(); is more readable then: Fruit.Create(); – Frederik Gheysels Sep 05 '09 at 06:14
  • I can see that, though the 'pollution' issue is highly dependent on the requirements of the rest of the class (i.e., the type information may be needed elsewhere). For the purposes and limitations of this example, either solution seems quite sufficient. – Matt Hamsmith Sep 05 '09 at 17:58
  • Shouldn't you make the constructor private? – John Gietzen Sep 10 '09 at 13:13
  • @John - Yes, good catch. There should be a constructor defined in Apple that is scoped as non-public such that outside classes must use the factory method to create the object. It might need to be protected such that the Fruit class may access it. Fruit does not technically need a constructor since it is an abstract class. I will edit accordingly. – Matt Hamsmith Sep 10 '09 at 14:06
  • 2
    You can't make `Apple` constructor less than public, because `where T : Fruit, new()` dictates that `T` must have a public constructor. @Matt Hamsmith - Your code doesn't compile until you remove `protected Apple() { }` . I've already tested it in VS. – Tohid Feb 13 '13 at 21:47
  • 1
    @Tohid - You are correct. I have edited. This does expose Apple to be constructed without using the Fruit CreateInstance static factory method, however, but that seems unavoidable. – Matt Hamsmith Mar 24 '14 at 16:01
  • What if I need to pass parameters to CreateInstance, but the needed parameters might differ in the inherited class? – shadow_map Aug 03 '14 at 15:04
  • @shadow_map The OP states "Fruit will be inherited by other concrete classes (Apple, Orange), each of which must expose a standard factory method CreateInstance() to create and initialize an instance." It sounds like your question relates to non-standard CreateInstance() methods to initialize an instance. Your situation might warrant a completely different question and solution. – Matt Hamsmith Aug 04 '14 at 15:31
  • I would also like to point out that this can be called with simply `Apple.CreateInstance()` instead of `Fruit.CreateInstance()` – Blue0500 Jun 30 '16 at 23:58
  • The only real advantage to this design is you can call `Apple.CreateInstance()`. Otherwise why not just have `public static T CreateInstance() where T : Fruit` and call it like `Fruit.CreateInstance()`? I have used your suggested design before and ultimately stopped because it results in a lot of confusing code. And this is not just my opinion: https://blogs.msdn.microsoft.com/ericlippert/2011/02/03/curiouser-and-curiouser/ – Mr Anderson Jul 07 '16 at 22:27
20

Move the factory method out of the type, and put it in its own Factory class.

public abstract class Fruit
{
    protected Fruit() {}

    public abstract string Define();

}

public class Apple : Fruit
{
    public Apple() {}

    public override string Define()
    {
         return "Apple";
    }
}

public class Orange : Fruit
{
    public Orange() {}

    public override string Define()
    {
         return "Orange";
    }
}

public static class FruitFactory<T> 
{
     public static T CreateFruit<T>() where T : Fruit, new()
     {
         return new T();
     }
}

But, as I'm looking at this, there is no need to move the Create method to its own Factory class (although I think that it is preferrable -separation of concerns-), you can put it in the Fruit class:

public abstract class Fruit
{

   public abstract string Define();

   public static T CreateFruit<T>() where T : Fruit, new()
   {
        return new T();
   }

}

And, to see if it works:

    class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ());
            Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ());

            Console.ReadLine ();
        }        
    }
Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • 1
    Your code will not compile. you need the where T : new() clause. However, this is an exact dupe of mine. – John Gietzen Sep 04 '09 at 16:04
  • 1
    Although one could create a static FruitFactory class, I would favor an IFruitFactory interface, along with an instantiable FruitFactory class. You may end up with only one FruitFactory class, whose CreateFruit method makes no reference to its object instance and could thus just as well have been a static method, but if it ever becomes necessary to offer multiple ways of creating fruit, or if fruit creation ever requires the ability to maintain state, using instance methods may prove useful. – supercat Jun 27 '11 at 14:50
4

I would do something like this

 public abstract class Fruit() {
      public abstract void Initialize();
 }

 public class Apple() : Fruit {
     public override void Initialize() {

     }
 }

 public class FruitFactory<T> where T : Fruit, new {
      public static <T> CreateInstance<T>() {
          T fruit = new T();
          fruit.Initialize();
          return fruit;  
      }
 } 


var fruit = FruitFactory<Apple>.CreateInstance()
Bob
  • 97,670
  • 29
  • 122
  • 130
3

Why not create a factory class (templated) with a create method?

FruitFactory<Banana>.Create();
Daniel Rodriguez
  • 1,443
  • 12
  • 19
3

The WebRequest class and its derivative types in the .NET BCL represent a good example of how this sort of design can be implemented relatively well.

The WebRequest class has several sub-classes, including HttpWebRequest and FtpWebReuest. Now, this WebRequest base class is also a factory type, and exposes a static Create method (the instance constructors are hidden, as required by the factory pattern).

public static WebRequest Create(string requestUriString)
public static WebRequest Create(Uri requestUri)

This Create method returns a specific implementation of the WebRequest class, and uses the URI (or URI string) to determine the type of object to create and return.

This has the end result of the following usage pattern:

var httpRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com/");
// or equivalently
var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://stackoverflow.com/");

var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://stackoverflow.com/");
// or equivalently
var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://stackoverflow.com/");

I personally think this is a good way to approach the issue, and it does indeed seem to be the preffered method of the .NET Framework creators.

Noldorin
  • 144,213
  • 56
  • 264
  • 302
3

First of all, not having static initializers that can be virtual doesn't mean you can't have "standard" member methods, that could be overloaded. Second of all, you can call your virtual methods from constructors, and they will work as expected, so there's no problem here. Third of all, You can use generics to have type-safe factory.
Here's some code, that uses factory + member Initialize() method that is called by constructor (and it's protected, so you don't have to worry, that someone will call it again after creating an object):


abstract class Fruit
{
    public Fruit()
    {
        Initialize();
    }

    protected virtual void Initialize()
    {
        Console.WriteLine("Fruit.Initialize");
    }
}

class Apple : Fruit
{
    public Apple()
        : base()
    { }

    protected override void Initialize()
    {
        base.Initialize();
        Console.WriteLine("Apple.Initialize");
    }

    public override string ToString()
    {
        return "Apple";
    }
}

class Orange : Fruit
{
    public Orange()
        : base()
    { }

    protected override void Initialize()
    {
        base.Initialize();
        Console.WriteLine("Orange.Initialize");
    }

    public override string ToString()
    {
        return "Orange";
    }
}

class FruitFactory
{
    public static T CreateFruit<T>() where T : Fruit, new()
    {
        return new T();
    }
}

public class Program
{

    static void Main()
    {
        Apple apple = FruitFactory.CreateFruit<Apple>();
        Console.WriteLine(apple.ToString());

        Orange orange = new Orange();
        Console.WriteLine(orange.ToString());

        Fruit appleFruit = FruitFactory.CreateFruit<Apple>();
        Console.WriteLine(appleFruit.ToString());
    }
}
Marcin Deptuła
  • 11,789
  • 2
  • 33
  • 41
  • 1
    Generally it's best to avoid calling virtual methods from constructors. See http://blogs.msdn.com/abhinaba/archive/2006/02/28/540357.aspx – TrueWill Sep 04 '09 at 18:01
  • That's true, it can be tricky, although, it's possible and will always work as it should, the problem is, to understand what means 'it should'. In general, I tend to avoid to call any more complicated methods (even non-virtual, ie. because they are often constructed in a way, that allows them to throw an exception, and I don't like my ctors to throw anything), but that's developer's decision. – Marcin Deptuła Sep 04 '09 at 18:15
0

I'd say the best thing to do is to create a virtual/abstract Initialise method on the fruit class which must be called and then create an external 'fruit factory' class to create instances:


public class Fruit
{
    //other members...
    public abstract void Initialise();
}

public class FruitFactory()
{
    public Fruit CreateInstance()
    {
        Fruit f = //decide which fruit to create
        f.Initialise();

        return f;
    }
}
Lee
  • 142,018
  • 20
  • 234
  • 287
0

All these ideas compensate for a glaring lack of language with stilted language constructs.

If I want an apple, I don't call a method on the fruit. Why should I even learn the family tree of the class? And I don't want to repeat myself or copy constructors through the whole hierarchy like a madman. Computers were invented to save me from such nonsense.

Two languages that have noticed and are now doing it right are PHP and Swift. Both supported static inheritance and covariance.

And old holy OOP books with a supposedly pure doctrine you can burn, because everything that allows me elegant code and compiles is good.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 26 '22 at 07:23