16

I am trying to create a static method that would return an instance of the class, something like:

class A {
   public static A getInstance() {
      return new A();
   }
}

The problem I am having is that if I have a subclass B derived from A, I would like B.getInstance() to return an instance of B, and not A. In PHP world, you could use a keyword "self" to reference to your own type, so your getInstance() would look like:

public static function getInstance() {
   return new self();
}

What's the best way to go about this?

m1tk4
  • 3,439
  • 1
  • 22
  • 27
  • 1
    Only way I've ever done is use the CRTP to pass the derived type to the base type via generics. It's frowned upon here, so I won't discuss it further. Perhaps reflection and `Activator.CreateInstance()` will be your best bet? – siride Aug 23 '11 at 14:49

6 Answers6

8

You can't, basically. If a call to a static member which is only declared in a base class is actually expressed in terms of the derived class, like this:

// Urgh
Encoding ascii = ASCIIEncoding.ASCII;

// Worse yet (and yes, I've seen this)
Encoding ascii = UTF8Encoding.ASCII;

then the compiler silently transforms that to:

Encoding ascii = Encoding.ASCII;

The fact that the original source contained the name of a derived class is not preserved in the compiled code, so it can't be acted on at execution time.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Makes sense. PHP can do this since it's an interpreted language - the feature is actually called "late static binding" (http://php.net/manual/en/language.oop5.late-static-bindings.php). Thank you! – m1tk4 Aug 23 '11 at 15:03
  • I think `UTF8Encoding.Default` is even worse. At least with `.ASCII`, you should expect that it's actually ASCII. With `.Default`, it looks like the default variant of UTF-8, or something like that. – svick Aug 23 '11 at 20:52
  • @svick: True. `Default` is named badly to start with, mind you - it should be `SystemDefault` to distinguish it from "the default encoding used for most API calls" which is UTF-8. – Jon Skeet Aug 23 '11 at 20:55
7

How about using generics to provide the type to produce:

public static T getInstance<T>() where T : A, new()
{
    return new T();
}
George Duckett
  • 31,770
  • 9
  • 95
  • 162
5

You can hide and so in a way override the getInstance method in the inheriting type like this:

class B : A {
    public static new B getInstance() {
        return new B();
    }
}

The new keyword specifies that the hiding of the inherited method was intentional, otherwise you get a compiler warning.

Complete example:

using System;

class A {
    public static A getInstance() { return new A(); }
    public void PrintSelf() {Console.WriteLine(this.GetType().Name);}
}

class B : A {   
    public static new B getInstance() {
        return new B();
    }
}

public class MyClass {
    public static void Main() {
        A a = A.getInstance();
        B b = B.getInstance();
        a.PrintSelf();
        b.PrintSelf();
    }
}
Dio F
  • 2,458
  • 1
  • 22
  • 39
Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
1

There's no way to do this statically -- you just have to use the actual name.

However, I'll bet PHP does this dynamically (i.e., with reflection) -- in which case you can always use GetCurrentMethod to get information about the method and itse enclosing type.

That way you can do something like:

using System.Reflection;

class Test
{
    static object MakeTest()
    {
        return Activator.CreateInstance(
            MethodBase.GetCurrentMethod().DeclaringType);
    }

    void Hello() { Console.WriteLine("Hi!"); }

    static void Main()
    {
        dynamic test = MakeTest();
        test.Hello();
    }
}
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    I don't think this will work because the `DeclaringType` will be the base class (since the static method is there) and not the derived class. – siride Aug 23 '11 at 14:56
  • @siride: Great point -- I think I might have misunderstood the question then. It seems like the OP is looking for Python's class methods, which C# doesn't quite have. – user541686 Aug 23 '11 at 14:58
  • 1
    The PHP feature that allowed me to do this is called "late static binding" - http://php.net/manual/en/language.oop5.late-static-bindings.php . Apparently, there is no equivalent in C# world. – m1tk4 Aug 23 '11 at 15:02
1

Since others say there's no way to do this, I'll just offer up CRTP. It has its faults (Eric Lippert has a post about it here somewhere), but it will solve this problem

class Base<T> where T : Base<T>, new() {
    public static T GetInstance() {
        return new T();
    }
}

class Derived : Base<Derived> {
}

Derived d = Derived.GetInstance();

This does make Base<T> hard to use, however. It's best if Base<T> is abstract. You can also create a side class like this that can actually be instantiated and used:

class Base : Base<Base> {
}

It looks weird, but it works. Unfortunately, Base and Derived will not be related anymore (except by Base<T>) so you can't cast between them, as such.

siride
  • 200,666
  • 4
  • 41
  • 62
  • 1
    The post you're thinking of is probably this one: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx which was based on this SO answer: http://stackoverflow.com/questions/3783321/why-does-this-generic-constraint-compile-when-it-seems-to-have-a-circular-referen/3789193#3789193 – Eric Lippert Aug 23 '11 at 17:17
  • @Eric Lippert: that's the one. I both agree and disagree. You are logically correct, but sometimes it's just easier to use CRTP and demand that users do the right things where it could break. – siride Aug 24 '11 at 01:33
1

You can not override static member function in derived class. If you choose static approach, you need just add the same method to B class.

Sure you can use also different design patterns, like Factory for example, but I think this is out of scope of the question.

Tigran
  • 61,654
  • 8
  • 86
  • 123