如何:在C#中自动实例化Singleton

时间:2022-09-02 08:46:35

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

我希望有一个Singleton,它将在程序启动时自动实例化。

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.

我所说的“自动实例化”是指Singleton中的代码应该在程序启动时实例化,而不需要其他代码的任何调用或声明。

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

所以我希望在程序启动时实例化并写出“MySingleton Instantiated”之类的东西(没有主代码做任何事情)...

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.

除了这不起作用,因为C#只会在需要时初始化类的静态成员,即当它们被访问/等时。

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#.

我没有亲自使用C ++(暂时没有使用C ++),但我很确定它可以在C ++中完成但不确定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).

我真正想要做的就是......会有很多这些单例类(随着时间的推移可以添加更多),所有这些类都将从一个公共(抽象)父类继承(也就是说。 PClass)。

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

PClass将有一个静态成员,它是PClasses的集合......以及一个将自己添加到集合中的构造函数......

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.

然后从理论上讲,所有单例都会自动添加到集合中(因为当它们被实例化时,调用基类PClass构造函数并将新对象添加到集合中)...然后可以在不知道任何关于什么子项的情况下使用该集合(已经实现了单例类,并且可以随时添加新的子(单例)类,而无需更改任何其他代码。

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.

PS。是的,我知道单身人士和他们的使用有不好的感觉......但有时他们很有用,即使撒旦自己做单身人士,我仍然想知道我的问题是否可以在C#中实现。非常感谢大家。

8 个解决方案

#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:

Chris提到的IoC方法可能是最好的,但是我没能想到的“最佳”解决方案是用反射和属性做一些时髦的事情:

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);
        }
    }
}

[InitOnLoad]
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.

通过调用InitOnLoad.Initialise()添加到main方法中。

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).

您可以取消该属性,但这可能会导致不必要的类型被初始化并且不必要地消耗内存(例如上面代码中的Bar)。

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.

还值得注意的是,这不会处理动态加载的任何程序集中包含的类型,除非您再次调用Initialise,而是基于您的问题(“在程序启动时自动实例化”),这听起来不是问题为了你。

#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.

虽然.NET模块理论上可以(IIRC)对模块负载等作出反应,但这不能通过C#获得。在某些框架(如ASP.NET)中,您可以通过配置使用钩子,例如通过处理程序或通过global.asax.cs进行黑客攻击 - 但是,对于常规C#应用程序(控制台,winform等),您必须手动触发它。例如,将调用承载主入口点的类上的静态构造函数。

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

那么:这里的用例是什么?什么时候延迟加载方法不行?

#3


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

根据您的尝试,我会放弃真正的Singleton的想法,而是使用IoC库。

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

查看StructureMap,Castle Windsor,Ninject和/或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.

这将允许您通过IoC库创建类作为单例,具有您想要的任意数量,但它只是一个普通的旧类。

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.

只需对“Singleton Considered Harmful”进行谷歌搜索,您就会看到更多参考文献。

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

或者,您也可以使用简单的类工厂/方法工厂模式。

#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.

我怀疑如果没有从Main做任何事情,这是可能的。如果你不使用它,即使向类中添加静态MySingleton(){}也不能保证它的实例化。

#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.

你基本上要求.NET框架在加载程序集时调用某个函数。该函数将是PClass实例注册器。

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.

C#中没有DllMain。您可以通过在main方法中使用程序集级属性和一些代码来模拟这一点,该方法会在程序集加载时进行侦听。该属性可以指定“DllMain”入口点或指向您的PCIass继承类。

#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):

切向地,请允许我指出这不是真正实现的单例模式。这是C#中的常规(简化)实现(为简洁起见,不包括线程安全等内容):

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. =)

说了这么多,我同意那个询问你的用例的人。知道这很好。 =)

#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.

我不相信.Net框架中你想要的东西是可能的。基本上,您需要像运行时一样通知类型或调用某些预定义的静态方法,以便应用程序加载并运行或提供类似的机制。考虑一下开销。运行时唯一确保的是自动调用程序的主入口方法。而已。在那里你可以设置并初始化它们,但它没有任何自动。你仍然明确地挂钩并进行方法调用。

#8


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

“PClass会有一个静态成员,它是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.

听起来像是可以通过Reflection轻松完成的事情。您不需要在启动时运行的代码;您可以随时根据需要构建这些类的列表。

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.

这是一个在第一次读取Instances属性时将加载列表的示例,将查看当时加载的所有程序集(如果您只想查看与PClass相同的程序集,可以简化一下,但是问题没有指定你想要查看的程序集,并且会将任何PClass后代的单个实例(但不是PClass本身)添加到列表中。每个PClass后代都需要一个无参数构造函数,但是您不需要任何类构造函数。

public class PClass
{
    private static List<PClass> m_instances;
    public static IList<PClass> Instances
    {
        get
        {
            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))
                    m_instances.Add(Activator.CreateInstance(type));
            }
        }
    }
}

#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:

Chris提到的IoC方法可能是最好的,但是我没能想到的“最佳”解决方案是用反射和属性做一些时髦的事情:

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);
        }
    }
}

[InitOnLoad]
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.

通过调用InitOnLoad.Initialise()添加到main方法中。

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).

您可以取消该属性,但这可能会导致不必要的类型被初始化并且不必要地消耗内存(例如上面代码中的Bar)。

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.

还值得注意的是,这不会处理动态加载的任何程序集中包含的类型,除非您再次调用Initialise,而是基于您的问题(“在程序启动时自动实例化”),这听起来不是问题为了你。

#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.

虽然.NET模块理论上可以(IIRC)对模块负载等作出反应,但这不能通过C#获得。在某些框架(如ASP.NET)中,您可以通过配置使用钩子,例如通过处理程序或通过global.asax.cs进行黑客攻击 - 但是,对于常规C#应用程序(控制台,winform等),您必须手动触发它。例如,将调用承载主入口点的类上的静态构造函数。

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

那么:这里的用例是什么?什么时候延迟加载方法不行?

#3


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

根据您的尝试,我会放弃真正的Singleton的想法,而是使用IoC库。

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

查看StructureMap,Castle Windsor,Ninject和/或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.

这将允许您通过IoC库创建类作为单例,具有您想要的任意数量,但它只是一个普通的旧类。

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.

只需对“Singleton Considered Harmful”进行谷歌搜索,您就会看到更多参考文献。

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

或者,您也可以使用简单的类工厂/方法工厂模式。

#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.

我怀疑如果没有从Main做任何事情,这是可能的。如果你不使用它,即使向类中添加静态MySingleton(){}也不能保证它的实例化。

#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.

你基本上要求.NET框架在加载程序集时调用某个函数。该函数将是PClass实例注册器。

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.

C#中没有DllMain。您可以通过在main方法中使用程序集级属性和一些代码来模拟这一点,该方法会在程序集加载时进行侦听。该属性可以指定“DllMain”入口点或指向您的PCIass继承类。

#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):

切向地,请允许我指出这不是真正实现的单例模式。这是C#中的常规(简化)实现(为简洁起见,不包括线程安全等内容):

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. =)

说了这么多,我同意那个询问你的用例的人。知道这很好。 =)

#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.

我不相信.Net框架中你想要的东西是可能的。基本上,您需要像运行时一样通知类型或调用某些预定义的静态方法,以便应用程序加载并运行或提供类似的机制。考虑一下开销。运行时唯一确保的是自动调用程序的主入口方法。而已。在那里你可以设置并初始化它们,但它没有任何自动。你仍然明确地挂钩并进行方法调用。

#8


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

“PClass会有一个静态成员,它是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.

听起来像是可以通过Reflection轻松完成的事情。您不需要在启动时运行的代码;您可以随时根据需要构建这些类的列表。

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.

这是一个在第一次读取Instances属性时将加载列表的示例,将查看当时加载的所有程序集(如果您只想查看与PClass相同的程序集,可以简化一下,但是问题没有指定你想要查看的程序集,并且会将任何PClass后代的单个实例(但不是PClass本身)添加到列表中。每个PClass后代都需要一个无参数构造函数,但是您不需要任何类构造函数。

public class PClass
{
    private static List<PClass> m_instances;
    public static IList<PClass> Instances
    {
        get
        {
            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))
                    m_instances.Add(Activator.CreateInstance(type));
            }
        }
    }
}