Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- namespace ParnassianStudios.Services
- {
- using Utilities;
- /// <summary>
- /// The Service Locator. In a bout of meta-fancy, this locator service is actually,
- /// in itself, a service and can be retrieved like any other service. The majority
- /// of services are program-wide, automatically registered using the <seealso
- /// cref="ServiceAttribute"/> attached to the concrete provider for the service type.
- ///
- /// You can override these default service providers by attaching the ServiceAttribute
- /// to a class of your choice, with a priority number greater than zero. Services
- /// registered this way are treated as compile-time static and cannot be removed from
- /// the services system- they're retrievable anywhere, at any time, by using the static
- /// methods of this class.
- ///
- /// In addition to this functionality, you can create an instance of this locator
- /// service to use as your own personal service handler, for situations where you
- /// want services to not have application-wide accessibility, or interfere with
- /// any existing service-based operations in the program. The instance version
- /// can have additonal services registered and unregistered as needed, and it
- /// falls back to the static globally-provided services if an instanced registration
- /// of that type has not been made. In other words, it'll check for local services
- /// first, then global ones if none are found.
- ///
- /// An instance of the Service Locator with no additional service registrations
- /// is functionally identical to just using the static class methods.
- /// </summary>
- [Service(typeof(IServiceLocator))]
- public class ServiceLocator : IServiceLocator
- {
- private class ServiceData
- {
- public IService Instance { get; set; }
- public Type HandlerType { get; set; }
- public ServiceLifetime Lifetime { get; set; }
- public int Priority { get; set; }
- public ServiceData() { }
- public ServiceData(IService instance, Type handlerType, ServiceLifetime lifetime, int priority)
- {
- Instance = instance;
- HandlerType = handlerType;
- Lifetime = lifetime;
- Priority = priority;
- }
- }
- private static Dictionary<Type, ServiceData> mStaticServicesLookup;
- private Dictionary<Type, ServiceData> mLocalServicesLookup;
- static ServiceLocator()
- {
- mStaticServicesLookup = new Dictionary<Type, ServiceData>();
- foreach (Type handlerType in GetAllTypesWithAttribute<ServiceAttribute>(creatableTypesOnly: true))
- {
- foreach (ServiceAttribute serviceAttr in handlerType.GetCustomAttributes(typeof(ServiceAttribute), false))
- {
- foreach (Type serviceType in serviceAttr.ServiceTypes)
- RegisterService_INTERNAL(mStaticServicesLookup, serviceType, handlerType, serviceAttr.Lifetime, serviceAttr.Priority);
- }
- }
- }
- public ServiceLocator()
- {
- mLocalServicesLookup = new Dictionary<Type, ServiceData>();
- }
- // this is usually in a static helper class, included here for simplicity
- public static Type[] GetAllTypesWithAttribute(Type type, bool creatableTypesOnly = false)
- {
- if (!(typeof(Attribute)).IsAssignableFrom(type))
- return new Type[0];
- List<Assembly> assemblyList = AppDomain.CurrentDomain.GetAssemblies().ToList();
- IEnumerable<Type> types = assemblyList
- .SelectMany(t => t.GetTypes())
- .Where(t => t.GetCustomAttributes(type, true).Length > 0);
- if (creatableTypesOnly)
- {
- types = types
- .Where(t => !t.IsInterface && !t.IsAbstract && !(t.GetConstructor(Type.EmptyTypes) == null));
- }
- return types.ToArray();
- }
- /// <summary>
- /// Retrieves an instance of the service, if one is registered.
- /// Will only retrieve static services.
- /// </summary>
- public static ServiceType GetService<ServiceType>(bool returnNewInstance = false)
- where ServiceType : IService
- {
- return (ServiceType)GetService_INTERNAL(mStaticServicesLookup, typeof(ServiceType), returnNewInstance);
- }
- /// <summary>
- /// Retrieves an instance of the service, if one is registered.
- /// Will only retrieve static services.
- /// </summary>
- public static IService GetService(Type serviceType, bool returnNewInstance = false)
- {
- return GetService_INTERNAL(mStaticServicesLookup, serviceType, returnNewInstance);
- }
- /// <summary>
- /// Retrieves an instance of the service, if one is registered.
- /// Will try retrieving instance, then static services.
- /// </summary>
- ServiceType IServiceLocator.GetService<ServiceType>(bool returnNewInstance)
- {
- ServiceType inst = (ServiceType)GetService_INTERNAL(mLocalServicesLookup, typeof(ServiceType), returnNewInstance);
- if (inst == null)
- inst = (ServiceType)GetService_INTERNAL(mStaticServicesLookup, typeof(ServiceType), returnNewInstance);
- return inst;
- }
- /// <summary>
- /// Retrieves an instance of the service, if one is registered.
- /// Will try retrieving instance, then static services.
- /// </summary>
- IService IServiceLocator.GetService(Type serviceType, bool returnNewInstance)
- {
- IService inst = GetService_INTERNAL(mLocalServicesLookup, serviceType, returnNewInstance);
- if (inst == null)
- inst = GetService_INTERNAL(mStaticServicesLookup, serviceType, returnNewInstance);
- return inst;
- }
- private static IService GetService_INTERNAL(Dictionary<Type, ServiceData> lookup, Type serviceType, bool returnNewInstance)
- {
- if (!lookup.ContainsKey(serviceType))
- return null;
- ServiceData serviceData = lookup[serviceType];
- // if we're using instanced services here, just return a new instance
- if (returnNewInstance || serviceData.Lifetime == ServiceLifetime.Transient)
- return (IService)Activator.CreateInstance(serviceData.HandlerType);
- // if not, then we need to determine if an instance exists,
- // and return it or create a new one
- if (serviceData.Instance == null)
- {
- serviceData.Instance = (IService)Activator.CreateInstance(serviceData.HandlerType);
- // if the service scope is the current scene, then when the active scene changes
- // automatically unregister the service, as well as removing the delegate
- if (serviceData.Lifetime == ServiceLifetime.Scene)
- {
- UnityEngine.Events.UnityAction<UnityEngine.SceneManagement.Scene, UnityEngine.SceneManagement.Scene> del = null;
- UnityEngine.SceneManagement.SceneManager.activeSceneChanged += del = (x, y) =>
- {
- UnityEngine.SceneManagement.SceneManager.activeSceneChanged -= del;
- UnregisterService_INTERNAL(lookup, serviceData.HandlerType);
- };
- }
- }
- return serviceData.Instance;
- }
- /// <summary>
- /// Registers a given service provider to the system.
- /// </summary>
- void IServiceLocator.RegisterService<ServiceType, HandlerType>(ServiceLifetime lifetime)
- {
- RegisterService_INTERNAL(mLocalServicesLookup, typeof(ServiceType), typeof(HandlerType), lifetime, int.MaxValue);
- }
- /// <summary>
- /// Registers a given service provider to the system.
- /// </summary>
- void IServiceLocator.RegisterService(Type serviceType, Type handlerType, ServiceLifetime lifetime)
- {
- RegisterService_INTERNAL(mLocalServicesLookup, serviceType, handlerType, lifetime, int.MaxValue);
- }
- private static void RegisterService_INTERNAL(Dictionary<Type, ServiceData> lookup, Type serviceType, Type handlerType, ServiceLifetime lifetime, int priority)
- {
- // if there's already a service registered for this service type
- if (lookup.ContainsKey(serviceType))
- {
- ServiceData data = lookup[serviceType];
- // if the priority is too low to override, just quit
- if (priority < data.Priority)
- return;
- data.Priority = priority;
- data.Lifetime = lifetime;
- // if the handler is changing, drop any existing instance
- if (data.HandlerType != handlerType)
- {
- data.HandlerType = handlerType;
- IService inst = data.Instance;
- data.Instance = null;
- inst?.OnServiceUnregisteredEvent(null, null);
- }
- return;
- }
- // if we're making a new service from scratch
- lookup[serviceType] = new ServiceData()
- {
- Instance = null,
- HandlerType = handlerType,
- Lifetime = lifetime,
- Priority = priority
- };
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement