0

Как часть попытки предложить ответ на другой вопрос, я хотел создать Dictionary саморегистрационных экземпляров Singleton. В частности, что-то вроде этого:Яркая загрузка и хранение 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.

Любые предложения? Это даже хороший дизайн для доступа к синглтонам по строке?


Строка кода, чтобы проверить результат является:

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

Это успешным, если вы не получите KeyNotFound исключение (или null).

Там может быть явной инициализацией Role (например, Role.Initialize()), но не подклассы - идея, чтобы иметь возможность добавить подкласс поэтому словарь имеет его, не возникает необходимости менять что-либо предварительное существующий.

+0

Причина, по которой вы не хотите менять словарь в 'Role' - вы не хотите вносить изменения в каждый новый подтип или не хотите делать изменения когда-либо? –

+0

@nsinreal - Первый. Я хочу иметь возможность создать новый тип «Роль», не делая ничего более, чем подклассифицируя его. – Bobson

+0

Хорошо, поймите. Reflection –

ответ

1

Hm .. Существует реальная проблема, что пользователь может создать свои AppDomains. Нет хорошего способа получить все загруженные AppDomains в текущем процессе. Я использую плохой хак, основанный в сети: 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"; 
    } 

} 

Что мы делаем: мы перебираем все AppDomains, получая от них все Ассембли. Для каждой сборки мы пытаемся найти тип Key + "Role" (на основе соглашения), проверяя, что проблем нет, получите поле «Экземпляр».

Теперь о взломе: это плохая идея. Лучше, если вы создадите синглтон со списком всех загруженных доменов. При создании домена вы должны добавить его в список, при выгрузке - удалить из списка и удалить все типы, принадлежащие домену из ролей и других классов. Как это работает сейчас: нет возможности разгрузить AppDomain, потому что всегда существует ссылка на один из его типов. Если вам не нужно выгружать AppDomains, вы можете оставить этот код, как он wrotten.

Если вы никогда не создадите AppDomain, вы можете выполнять итерацию только через AppDomain.CurrentDomain сборок.

+0

Ничего себе. Это впечатляет. Я не думаю, что я дам ответ на [другой вопрос] (http://programmers.stackexchange.com/questions/204550/how-can-i-improve-this-design-so-that- i-dont-need-to-dynamic-scan-classes-a) если это то, что он собирается предпринять, чтобы заставить его работать, но я обязательно свяжу его здесь. – Bobson

+0

@ Бобсон, и это действительно уродливо. Есть некоторые проблемы с Mono и другими вещами, такими как LinqPad. И это официально не поддерживается, поэтому ад знает, что может случиться. Попытайтесь подумать о создании статического метода 'FindRoles (assembly)' –

+0

@Bobson Если может быть много подсетей (по крайней мере два), вы можете сделать некоторый статический класс AppDomainHelper с LoadDomain() и UnloadDomain() и события DomainLoaded, DomainBeforeUnload , Надеюсь, вы понимаете, что делать дальше. Вопросов? –

Смежные вопросы