Skip to content

Instantly share code, notes, and snippets.

@efruchter
Last active January 2, 2019 20:01
Show Gist options
  • Save efruchter/4676977a2b3b7daa1c09e9bb29c48c23 to your computer and use it in GitHub Desktop.
Save efruchter/4676977a2b3b7daa1c09e9bb29c48c23 to your computer and use it in GitHub Desktop.
My service locator plus injector boilercode. Requires C#7 for some syntax sugar, but can be backported.
namespace Common
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
public static class ServiceLocator
{
public static Dictionary<Type, object> _serviceMap = new Dictionary<Type, object>();
public static Dictionary<Type, FieldInfo[]> _propertyCache = new Dictionary<Type, FieldInfo[]>();
public static LinkedList<object> _typeLessInstances = new LinkedList<object>();
public static T BindAsType<T>(T service) where T : class
{
return BindAsType(typeof(T), service) as T;
}
public static object BindAsType(Type bindingType, object service)
{
var serviceAlreadyExists = _serviceMap.ContainsKey(bindingType);
Debug.Assert(!serviceAlreadyExists, $"Binding an instance to a service that is already bound. {bindingType}");
_serviceMap[bindingType] = service;
if (!serviceAlreadyExists)
{
if (service is UnityEngine.Object)
{
Debug.Log($"ServiceLocator: Binding instance to service type {bindingType}", service as UnityEngine.Object);
}
else
{
Debug.Log($"ServiceLocator: Binding instance to service type {bindingType}");
}
}
return service;
}
public static object BindTypeless(object service)
{
_typeLessInstances.AddLast(service);
return service;
}
public static T Locate<T>() where T : class
{
return Locate(typeof(T)) as T;
}
public static T Locate<T>(ref T t) where T : class
{
return t = Locate(typeof(T)) as T;
}
private static object Locate(Type type)
{
var serviceFound =_serviceMap.TryGetValue(type, out var t);
if (!serviceFound)
{
foreach (var _instance in _serviceMap.Values)
{
if (type.IsAssignableFrom(_instance.GetType()))
{
return BindAsType(type, _instance);
}
}
foreach (var _instance in _typeLessInstances)
{
if (type.IsAssignableFrom(_instance.GetType()))
{
return BindAsType(type, _instance);
}
}
}
return t;
}
public static void Inject<T>(out T t) where T : class
{
t = Locate<T>();
}
public static void Inject(object target)
{
Type t = target.GetType();
if (!_propertyCache.TryGetValue(t, out FieldInfo[] properties))
{
properties = target.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Where(prop => Attribute.IsDefined(prop, typeof(InjectService)))
.ToArray();
_propertyCache[t] = properties;
}
foreach (var prop in properties)
{
prop.SetValue(target, Locate(prop.FieldType));
}
}
}
[AttributeUsage(AttributeTargets.Field)]
public class InjectService : Attribute
{
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment