How To: Auto Instantiate Singleton in C#

How To: Auto Instantiate Singleton in C#

多情癖 发布于 2021-11-30 字数 2218 浏览 956 回复 8 原文

I want to have a Singleton that will be auto instantiated on program start.

What I mean by "auto instantiated" is that the code in the Singleton should instantiate itself on program start without any calls or declarations by other code.

So I want something like the following to instantiate and write out "MySingleton Instantiated" on program start (without the main code doing anything)...

static class MySingleton
    private static MySingleton self = new MySingleton();

    protected MySingleton()
        System.Console.WriteLine("MySingleton Instantiated");

except this doesn't work since C# will only initialize the static members of a class when needed, ie when they are accessed/etc.

So what do I do? can this be done?

I haven't done this personally with C++ (haven't been using C++ for a while) but I'm pretty sure it can be done in C++ but not sure about C#.

Any help is appreciated. Thanks.

What I'm actually wanting to do with this is...
There would be many of these singleton classes (and more can be added as time goes on), all of which would inherit from a common (abstract) parent class (aka. PClass).

The PClass would have a static member that is a collection of PClasses... and a constructor to add itself to the collection...

Then in theory all the singletons would automagically be added to the collection (since when they are instantiated the base PClass constructor is called and adds the new object to the collection)... then the collection can be used without knowing anything about what child (singleton) classes have been implemented, and new child (singleton) classes can be added any time without having to change any other code.

Unfortunately I can't get the children (singletons) to instantiate themselves... screwing up my little plan, resulting in this post.

Hope I explained that well enough.

PS. Yes I realize there are bad feelings around Singletons and their use... but they are useful sometimes, and even if Satan himself made Singletons I'd still like to know if my problem can be achieved in C#. Thanks kindly to you all.

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。



需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。


策马西风 2022-06-07 8 楼

"The PClass would have a static member that is a collection of PClasses..."

Sounds like something that could be done easily with Reflection. You don't need code that runs on startup; you can just go build a list of these classes whenever you need it.

Here's an example that will load the list once when you first read the Instances property, will look in all assemblies that are loaded at that time (you could simplify this a bit if you only wanted to look in the same assembly as PClass, but your question didn't specify which assemblies you wanted to look in), and will add a single instance of any PClass descendant (but not PClass itself) to the list. Each PClass descendant will need a parameterless constructor, but you won't need any class constructors.

public class PClass
    private static List<PClass> m_instances;
    public static IList<PClass> Instances
            if (m_instances == null)
                m_instances = LoadInstanceList();
            return m_instances;
    private static List<PClass> LoadInstanceList()
        foreach (var assembly in AppDomain.GetAssemblies())
            foreach (var type in assembly.GetTypes())
                if (type.IsAssignableTo(typeof(PClass)) && type != typeof(PClass))
姜生凉生 2022-06-07 7 楼

I don't believe that what you want is possible in .Net framework. Basically, you want something like the runtime to notify types or call some predefined static method on them that the application is loaded and running or provide a mechanism like that. Think about the overhead. The only thing the runtime ensures is to automatically call the main entry method for your program. That's it. There you can set things up and initialize them but there is nothing automatic about it. You're still explicitly hooking things up and making method calls.

猫弦 2022-06-07 6 楼

Tangentially, allow me to point out that this isn't really the singleton pattern as it is normally implemented. Here's the normal (simplified) implementation in C# (not including stuff like thread-safety for brevity):

public sealed class Singleton
static readonly Singleton _instance = new Singleton();

// private ctor
Singleton() {}

 public static Singleton Instance
  get { return _instance; }

This line from your code shouldn't even compile!

protected MySingleton()

Having said all that, I agree with the guy who inquired as to your use-case. That'd be good to know. =)

聆听风音 2022-06-07 5 楼

You're basically asking the .NET framework to call some function whenever it loads an assembly. That function would be the PClass instances registrar.

There is no DllMain in C#. You can simulate this by having an assembly-level attribute and some code in your main method, which listens to whenever an assembly gets loaded. The attribute can have a "DllMain" entry point specified or point to your PCIass inheritted classes.

五里雾 2022-06-07 4 楼

I doubt this is possible without doing anything from Main. Even adding a static MySingleton() {} to the class does not guarantee its instantiation if you don't use it.

快乐很简单 2022-06-07 3 楼

Based on what you are trying to do, I would drop the idea of a true Singleton, and use an IoC library instead.

Check out StructureMap, Castle Windsor, Ninject, and/or Autofac.

This will allow you to create class as a singleton, via the IoC library, have as many as you want, but it is just a plain old class.

Singletons have an issue in that they really mess up the testability (via unit testing) of your application.

Just do a google search on "Singleton Considered Harmful" and you will see many more references.

Alternatively, you can also use a simple Class Factory/Method factory pattern.

岁月静好 2022-06-07 2 楼

While .NET modules can in theory (IIRC) react to module load etc, this isn't available via C#. In some frameworks (like ASP.NET) there are hooks you can use via configuration, such as hacking it via a handler or via global.asax.cs - however, for a regular C# app (console, winform etc) you would have to trigger it manually. For example, a static constructor on the class that hosts your Main entry point would get invoked.

So: what is the use-case here? When wouldn't the lazy loading approach be OK?

临风闻羌笛 2022-06-07 1 楼

The IoC approach mentioned by Chris is probably the best, but failing that the "best" solution I can think of is to do something funky with reflection and attributes along the lines of:

public class InitOnLoad : Attribute 
    public static void Initialise()
        // get a list of types which are marked with the InitOnLoad attribute
        var types = 
            from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
            where t.GetCustomAttributes(typeof(InitOnLoad), false).Count() > 0
            select t;

        // process each type to force initialise it
        foreach (var type in types)
            // try to find a static field which is of the same type as the declaring class
            var field = type.GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(f => f.FieldType == type).FirstOrDefault();
            // evaluate the static field if found
            if (field != null) field.GetValue(null);

public class Foo
    public static Foo x = new Foo();

    private Foo()
        Console.WriteLine("Foo is automatically initialised");

public class Bar
    public static Bar x = new Bar();

    private Bar()
        Console.WriteLine("Bar is only initialised as required");

With a call to InitOnLoad.Initialise() added to your main method.

You could do away with the attribute, but this may cause unnecessary types to be initialized and needlessly consume memory (such as Bar in the above code).

It's also worth noting that this won't handle types contained in any assemblies which are loaded dynamically, unless you make another call to Initialise, but based on your question ("auto instantiated on program start") that doesn't sound like an issue for you.