急切加载和存储Singleton

时间:2013-07-12 17:59:59

标签: c# design-patterns initialization singleton static-constructor

作为尝试提出另一个问题答案的一部分,我想创建一个Dictionary自注册的Singleton实例。具体来说,这样的事情:

public abstract class Role
{
    public static Dictionary<string, Role> Roles = new Dictionary<string, Role>(); 

    protected Role()
    {
        _roles.Add(this.Name, this);
    }
    public abstract string Name { get; }
}

public class AdminRole : Role
{
    public static readonly AdminRole Instance = new AdminRole();
    public override string Name { get { return "Admin"; } }
}

但是,除非我访问AdminRole,否则不会调用Instance构造函数,因此它不会被添加到Roles字典中。我知道我可以使用{ AdminRole.Instance.Name, Admin Role}来实例化字典,但是我想添加新角色以不需要Role类进行更改。

有什么建议吗?这甚至是通过字符串访问Singletons的好设计吗?


测试结果的代码行是:

var role = Role.Roles["Admin"];

如果您没有KeyNotFound例外(或null),则会成功。

可以Role的显式初始化(​​例如Role.Initialize()),而不是子类的初始化 - 想法是能够添加子类,以便字典有它,而不需要改变任何预先存在的东西。

1 个答案:

答案 0 :(得分:1)

嗯..确实有问题,用户可以创建他的AppDomains。没有好办法在当前进程中获取所有已加载的AppDomain。我在网络中使用了糟糕的黑客:Hot to get list of all AppDomains inside current process?。结果代码:

public static void Main()
{
    Console.WriteLine(Role.GetRole("Admin").Ololo);
}

public static class AppDomainExtensions {
    public static List<AppDomain> GetAllAppDomains() {
        List<AppDomain> appDomains = new List<AppDomain>();

        IntPtr handle = IntPtr.Zero;
        ICorRuntimeHost host = (ICorRuntimeHost)(new CorRuntimeHost());
        try
        {
            host.EnumDomains(out handle);
            while (true)
            {
                object domain;
                host.NextDomain(handle, out domain);
                if (domain == null)
                    break;
                appDomains.Add((AppDomain)domain);
            }
        }
        finally
        {
            host.CloseEnum(handle);
        }

        return appDomains;
    }

    [ComImport]
    [Guid("CB2F6723-AB3A-11d2-9C40-00C04FA30A3E")]
    private class CorRuntimeHost// : ICorRuntimeHost
    {
    }

    [Guid("CB2F6722-AB3A-11D2-9C40-00C04FA30A3E")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ICorRuntimeHost
    {
        void CreateLogicalThreadState ();
        void DeleteLogicalThreadState ();
        void SwitchInLogicalThreadState ();
        void SwitchOutLogicalThreadState ();
        void LocksHeldByLogicalThread ();
        void MapFile ();
        void GetConfiguration ();
        void Start ();
        void Stop ();
        void CreateDomain ();
        void GetDefaultDomain ();
        void EnumDomains (out IntPtr enumHandle);
        void NextDomain (IntPtr enumHandle, [MarshalAs(UnmanagedType.IUnknown)]out object appDomain);
        void CloseEnum (IntPtr enumHandle);
        void CreateDomainEx ();
        void CreateDomainSetup ();
        void CreateEvidence ();
        void UnloadDomain ();
        void CurrentDomain ();
    }   
}

public abstract class Role
{
    private static Dictionary<string, Role> Roles = new Dictionary<string, Role>(); 

    public static Role GetRole(string key) {
        if (Roles.ContainsKey(key))
            return Roles[key];

        foreach (var appDomain in AppDomainExtensions.GetAllAppDomains()) {
            foreach (var assembly in appDomain.GetAssemblies()) {
                var type = assembly.GetTypes().Where(t => t.Name == key + "Role").FirstOrDefault();// (key + "Role", false, true);              

                if (type == null || !typeof(Role).IsAssignableFrom(type))
                    continue;

                Role role = null;

                {
                    var fieldInfo = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public);


                    if (fieldInfo != null) {
                        role = fieldInfo.GetValue(null) as Role;               
                    }
                    else {
                        var propertyInfo = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);

                        if (propertyInfo != null)
                            role = propertyInfo.GetValue(null, null) as Role;
                    }
                }

                if (role == null)
                    continue;

                Roles[key] = role;

                return role;
            }           
        }

        throw new KeyNotFoundException();
    }

    public string Ololo {get;set;}
}

public class AdminRole : Role
{
    public static readonly AdminRole Instance = new AdminRole();

    private AdminRole() {
        Ololo = "a";
    }

}

我们做什么:我们遍历所有AppDomain,从中获取所有程序集。对于每个程序集,我们尝试查找类型Key + "Role"(基于约定),检查没有问题,获取“实例”字段。

现在,关于黑客:这是个坏主意。更好的是,如果您将创建包含所有已加载域列表的单例。在创建域时,必须将其添加到列表中,在卸载时 - 从列表中删除并从角色和其他类中删除属于域的所有类型。它现在如何工作:没有可能卸载AppDomain,因为它的类型之一总是有一个链接。如果您不需要卸载AppDomains,则可以保留此代码。

如果您永远不会创建AppDomain,则只能通过AppDomain.CurrentDomain程序集进行迭代。