作为尝试提出另一个问题答案的一部分,我想创建一个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()
),而不是子类的初始化 - 想法是能够添加子类,以便字典有它,而不需要改变任何预先存在的东西。
答案 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
程序集进行迭代。