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

时间:2009-04-27 21:41:42

标签: c# singleton

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

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

所以我希望在程序启动时实例化并写出“MySingleton Instantiated”,而不是主代码做任何事情......

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

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

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

那我该怎么办?可以这样做吗?

我没有亲自使用C ++(暂时没有使用C ++),但我很确定它可以在C ++中完成但不确定C#。

感谢任何帮助。感谢。


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

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

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

不幸的是,我无法让孩子们(单身人士)自我实例化......搞砸了我的小计划,导致了这篇文章。

希望我解释得那么好。


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

8 个答案:

答案 0 :(得分:6)

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

调用InitOnLoad.Initialise()添加到主方法。

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

根据你要做的事情,我会放弃真正的Singleton的想法,而是使用IoC库。

查看StructureMap,Castle Windsor,Ninject和/或Autofac。

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

单身者有一个问题,他们真的搞乱了你的应用程序的可测试性(通过单元测试)。

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

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

// private ctor
Singleton() {}

 public static Singleton Instance
 {
  get { return _instance; }
 }
}

您的代码中的这一行甚至不应该编译!

protected MySingleton()

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

“PClass将有一个静态成员,它是PClasses的集合......”

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

这是一个在第一次读取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));
            }
        }
    }
}