Instantiating types with no public constructors

Instantiating types with no public constructors

  • Comments 7

It turns out, Activator.CreateInstance and Activator.CreateInstance<T> fail if there is no public parameterless constructor defined on a type we’re trying to instantiate. Even if the constructor is internal (basically, anything other than public), CreateInstance will fail. This one was a surprise actually, I would expect the reflection binder to work for internal constructors at least.

Update: it turns out, there is an overload of Activator.CreateInstance(Type, bool) that does exactly what I want. Don’t use what I posted below :) Thanks to Andrey Shchekin for the tip!

Nevertheless, a more powerful alternative to Activator.CreateInstance in this case is to find the non-public constructor and invoke it manually:

using System;
using System.Reflection;

class Test
{
    private Test() { }
}

class Program
{
    static void Main(string[] args)
    {
        var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
        var ctor = typeof(Test).GetConstructor(flags, null, new Type[0], null);
        var instance = ctor.Invoke(null);

        //var instance = Activator.CreateInstance<Test>(); // Exception: No parameterless constructor defined for this object.
        //var instance = Activator.CreateInstance(typeof(Test)); // Exception: No parameterless constructor defined for this object.
    }
}

Why do I need this? Well, there is a class in our codebase that everyone was instantiating all over the place. But this class should have inherently been a singleton. I changed the code all over the place to use a singleton instead of instantiating it and I wanted to prohibit people to instantiate this class in the future.

We have a little Singleton helper that essentially looks like this:

public class Singleton<T> where T : class
{
    static T instance;
    public static T Instance
    { 
        get
        {
            if (instance == null)
            {
                instance = Instantiate();
            }
            return instance;
        }
    }

    static T Instantiate()
    {
        return Activator.CreateInstance<T>();
    }
}

So I would just inherit the class that everyone was using from Singleton like this: CommonClass : Singleton<CommonClass> and make the constructor private so that people can’t instantiate it manually anymore. However this became a problem because the Singleton’s Instantiate() method failed for non-public constructors. Now that I’ve used the approach above, it works fine. Singleton can instantiate the class, but no one else can.

 

P.S. Yes, I know about the right way to implement a singleton, I know about double-check locking and threading and that reflection is bad. But this solves the problem.

P.P.S. Yes, I know about burning the base class and that mixing in a singleton via inheritance is generally a bad idea. It works for us and does it’s job.

P.P.P.S. It occurred to me that if we had extension properties, one could have just made Instance an extension property on every type (that implements a certain “marker” interface). But there is no way currently to mix-in state into existing types (if you don’t look at WPF and their attached properties and dependency properties).

  • There is no right way to implement a singleton because it is an anti-pattern. :)

    Right way is to use the dependency injection, which solves the singleton problem and constructor dependency problems if there are any.

    By the way, I would use Activator.CreateInstance(typeof(Test), true).

    It seems to work for your example.

  • If you really want to confuse people, you can create an instance of any type without running any of its constructors!

    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getuninitializedobject.aspx

    Probably only intended for situations where you save the field values of an object, then you want a "blank" object you can load those values into.

  • No need to Daniel, I'm confused enough. :-)

  • Andrey:

    agreed to all. Dependency Injection is the way to go.

    BTW I didn't know about the overload of CreateInstance that accepts a Type and a bool:

    http://msdn.microsoft.com/en-us/library/he47tyc4.aspx

    And it indeed does work! Awesome!

    Thanks,

    Kirill

  • No, the singleton is not an "anti-pattern". Dependency injection is "neat" but it is not always practical or necessary. The real world is not like school - there aren't right and wrong answers, "always" and "nevers" do not apply. I know from personal experience that our project's use of the CAB IoC container where simpler solutions like static properties could have been used set us back tremendously. If you really need the flexibility that a full-fledged IoC container provides, it may be because you have poorly designed classes that do not share code and expose common interfaces.

  • Its funny. I got into this email thread from people at work about singleton pattern. My personal opinion is that singleton being construction pattern should not overlap with inheritance pattern. Hence it is more of a non-overlapping design pattern (you don't inherit from class factories right :)). Secondly, your example does not handle threading issues. Now if you add a static constructor and inherit from singleton class, then any static method (or first class reference) will invoke static constructor. If an exception is thrown, there is no fixed place to deal with it as well :). Usually I use a Singleton<T> class with a private class to perform thread safe, lazy load, traceable version of singleton (no other place than reference to Singleton<T>.Instance can exception be thrown). I also prefer protected (vs private) constructor in constructed class definition so inheritance hierarchy is not disturbed. Finally, if you have this non-overlapping pattern (without inheritance) than your target class does not need to make any assumptions about singleton construction (other than just being threadsafe probably) and is more testable (no static instances in tests). I will post a blog about it soon and you can link to it!

  • Thank u for this post - it really helped me :)

Page 1 of 1 (7 items)
Leave a Comment
  • Please add 6 and 4 and type the answer here:
  • Post