ParnassianStudios

ServiceLocator.cs

Jun 1st, 2019
49
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine;
  6.  
  7. namespace ParnassianStudios.Services
  8. {
  9.     using Utilities;
  10.  
  11.     /// <summary>
  12.     /// The Service Locator. In a bout of meta-fancy, this locator service is actually,
  13.     /// in itself, a service and can be retrieved like any other service. The majority
  14.     /// of services are program-wide, automatically registered using the <seealso
  15.     /// cref="ServiceAttribute"/> attached to the concrete provider for the service type.
  16.     ///
  17.     /// You can override these default service providers by attaching the ServiceAttribute
  18.     /// to a class of your choice, with a priority number greater than zero. Services
  19.     /// registered this way are treated as compile-time static and cannot be removed from
  20.     /// the services system- they're retrievable anywhere, at any time, by using the static
  21.     /// methods of this class.
  22.     ///
  23.     /// In addition to this functionality, you can create an instance of this locator
  24.     /// service to use as your own personal service handler, for situations where you
  25.     /// want services to not have application-wide accessibility, or interfere with
  26.     /// any existing service-based operations in the program. The instance version
  27.     /// can have additonal services registered and unregistered as needed, and it
  28.     /// falls back to the static globally-provided services if an instanced registration
  29.     /// of that type has not been made. In other words, it'll check for local services
  30.     /// first, then global ones if none are found.
  31.     ///
  32.     /// An instance of the Service Locator with no additional service registrations
  33.     /// is functionally identical to just using the static class methods.
  34.     /// </summary>
  35.     [Service(typeof(IServiceLocator))]
  36.     public class ServiceLocator : IServiceLocator
  37.     {
  38.         private class ServiceData
  39.         {
  40.             public IService Instance { get; set; }
  41.             public Type HandlerType { get; set; }
  42.             public ServiceLifetime Lifetime { get; set; }
  43.             public int Priority { get; set; }
  44.  
  45.             public ServiceData() { }
  46.             public ServiceData(IService instance, Type handlerType, ServiceLifetime lifetime, int priority)
  47.             {
  48.                 Instance = instance;
  49.                 HandlerType = handlerType;
  50.                 Lifetime = lifetime;
  51.                 Priority = priority;
  52.             }
  53.         }
  54.  
  55.         private static Dictionary<Type, ServiceData> mStaticServicesLookup;
  56.         private Dictionary<Type, ServiceData> mLocalServicesLookup;
  57.  
  58.  
  59.         static ServiceLocator()
  60.         {
  61.             mStaticServicesLookup = new Dictionary<Type, ServiceData>();
  62.             foreach (Type handlerType in GetAllTypesWithAttribute<ServiceAttribute>(creatableTypesOnly: true))
  63.             {
  64.                 foreach (ServiceAttribute serviceAttr in handlerType.GetCustomAttributes(typeof(ServiceAttribute), false))
  65.                 {
  66.                     foreach (Type serviceType in serviceAttr.ServiceTypes)
  67.                         RegisterService_INTERNAL(mStaticServicesLookup, serviceType, handlerType, serviceAttr.Lifetime, serviceAttr.Priority);
  68.                 }
  69.             }
  70.         }
  71.  
  72.         public ServiceLocator()
  73.         {
  74.             mLocalServicesLookup = new Dictionary<Type, ServiceData>();
  75.         }
  76.  
  77.  
  78.         // this is usually in a static helper class, included here for simplicity
  79.         public static Type[] GetAllTypesWithAttribute(Type type, bool creatableTypesOnly = false)
  80.         {
  81.             if (!(typeof(Attribute)).IsAssignableFrom(type))
  82.                 return new Type[0];
  83.  
  84.             List<Assembly> assemblyList = AppDomain.CurrentDomain.GetAssemblies().ToList();
  85.             IEnumerable<Type> types = assemblyList
  86.                 .SelectMany(t => t.GetTypes())
  87.                 .Where(t => t.GetCustomAttributes(type, true).Length > 0);
  88.  
  89.             if (creatableTypesOnly)
  90.             {
  91.                 types = types
  92.                     .Where(t => !t.IsInterface && !t.IsAbstract && !(t.GetConstructor(Type.EmptyTypes) == null));
  93.             }
  94.  
  95.             return types.ToArray();
  96.         }
  97.  
  98.  
  99.         /// <summary>
  100.         /// Retrieves an instance of the service, if one is registered.
  101.         /// Will only retrieve static services.
  102.         /// </summary>
  103.         public static ServiceType GetService<ServiceType>(bool returnNewInstance = false)
  104.             where ServiceType : IService
  105.         {
  106.             return (ServiceType)GetService_INTERNAL(mStaticServicesLookup, typeof(ServiceType), returnNewInstance);
  107.         }
  108.  
  109.         /// <summary>
  110.         /// Retrieves an instance of the service, if one is registered.
  111.         /// Will only retrieve static services.
  112.         /// </summary>
  113.         public static IService GetService(Type serviceType, bool returnNewInstance = false)
  114.         {
  115.             return GetService_INTERNAL(mStaticServicesLookup, serviceType, returnNewInstance);
  116.         }
  117.  
  118.         /// <summary>
  119.         /// Retrieves an instance of the service, if one is registered.
  120.         /// Will try retrieving instance, then static services.
  121.         /// </summary>
  122.         ServiceType IServiceLocator.GetService<ServiceType>(bool returnNewInstance)
  123.         {
  124.             ServiceType inst = (ServiceType)GetService_INTERNAL(mLocalServicesLookup, typeof(ServiceType), returnNewInstance);
  125.  
  126.             if (inst == null)
  127.                 inst = (ServiceType)GetService_INTERNAL(mStaticServicesLookup, typeof(ServiceType), returnNewInstance);
  128.  
  129.             return inst;
  130.         }
  131.  
  132.         /// <summary>
  133.         /// Retrieves an instance of the service, if one is registered.
  134.         /// Will try retrieving instance, then static services.
  135.         /// </summary>
  136.         IService IServiceLocator.GetService(Type serviceType, bool returnNewInstance)
  137.         {
  138.             IService inst = GetService_INTERNAL(mLocalServicesLookup, serviceType, returnNewInstance);
  139.  
  140.             if (inst == null)
  141.                 inst = GetService_INTERNAL(mStaticServicesLookup, serviceType, returnNewInstance);
  142.  
  143.             return inst;
  144.         }
  145.  
  146.         private static IService GetService_INTERNAL(Dictionary<Type, ServiceData> lookup, Type serviceType, bool returnNewInstance)
  147.         {
  148.             if (!lookup.ContainsKey(serviceType))
  149.                 return null;
  150.  
  151.             ServiceData serviceData = lookup[serviceType];
  152.  
  153.             // if we're using instanced services here, just return a new instance
  154.             if (returnNewInstance || serviceData.Lifetime == ServiceLifetime.Transient)
  155.                 return (IService)Activator.CreateInstance(serviceData.HandlerType);
  156.  
  157.             // if not, then we need to determine if an instance exists,
  158.             //  and return it or create a new one
  159.             if (serviceData.Instance == null)
  160.             {
  161.                 serviceData.Instance = (IService)Activator.CreateInstance(serviceData.HandlerType);
  162.  
  163.                 // if the service scope is the current scene, then when the active scene changes
  164.                 // automatically unregister the service, as well as removing the delegate
  165.                 if (serviceData.Lifetime == ServiceLifetime.Scene)
  166.                 {
  167.                     UnityEngine.Events.UnityAction<UnityEngine.SceneManagement.Scene, UnityEngine.SceneManagement.Scene> del = null;
  168.                     UnityEngine.SceneManagement.SceneManager.activeSceneChanged += del = (x, y) =>
  169.                        {
  170.                            UnityEngine.SceneManagement.SceneManager.activeSceneChanged -= del;
  171.                            UnregisterService_INTERNAL(lookup, serviceData.HandlerType);
  172.                        };
  173.                 }
  174.             }
  175.  
  176.             return serviceData.Instance;
  177.         }
  178.  
  179.         /// <summary>
  180.         /// Registers a given service provider to the system.
  181.         /// </summary>
  182.         void IServiceLocator.RegisterService<ServiceType, HandlerType>(ServiceLifetime lifetime)
  183.         {
  184.             RegisterService_INTERNAL(mLocalServicesLookup, typeof(ServiceType), typeof(HandlerType), lifetime, int.MaxValue);
  185.         }
  186.  
  187.         /// <summary>
  188.         /// Registers a given service provider to the system.
  189.         /// </summary>
  190.         void IServiceLocator.RegisterService(Type serviceType, Type handlerType, ServiceLifetime lifetime)
  191.         {
  192.             RegisterService_INTERNAL(mLocalServicesLookup, serviceType, handlerType, lifetime, int.MaxValue);
  193.         }
  194.  
  195.         private static void RegisterService_INTERNAL(Dictionary<Type, ServiceData> lookup, Type serviceType, Type handlerType, ServiceLifetime lifetime, int priority)
  196.         {
  197.             // if there's already a service registered for this service type
  198.             if (lookup.ContainsKey(serviceType))
  199.             {
  200.                 ServiceData data = lookup[serviceType];
  201.  
  202.                 // if the priority is too low to override, just quit
  203.                 if (priority < data.Priority)
  204.                     return;
  205.  
  206.                 data.Priority = priority;
  207.                 data.Lifetime = lifetime;
  208.  
  209.                 // if the handler is changing, drop any existing instance
  210.                 if (data.HandlerType != handlerType)
  211.                 {
  212.                     data.HandlerType = handlerType;
  213.  
  214.                     IService inst = data.Instance;
  215.                     data.Instance = null;
  216.  
  217.                     inst?.OnServiceUnregisteredEvent(null, null);
  218.                 }
  219.                 return;
  220.             }
  221.  
  222.             // if we're making a new service from scratch
  223.             lookup[serviceType] = new ServiceData()
  224.             {
  225.                 Instance = null,
  226.                 HandlerType = handlerType,
  227.                 Lifetime = lifetime,
  228.                 Priority = priority
  229.             };
  230.         }
  231.     }
  232. }
RAW Paste Data