Last active
August 29, 2015 14:03
-
-
Save Zeroto/7c3b15deda0121ff965e to your computer and use it in GitHub Desktop.
Basic DI container for Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using UnityEngine; | |
namespace UnityDI | |
{ | |
public class DIContainer | |
{ | |
/// <summary> | |
/// Create a new container | |
/// </summary> | |
/// <returns>The newly created container</returns> | |
static public DIContainer Create() | |
{ | |
return new DIContainer(); | |
} | |
private static Dictionary<string, DIContainer> containers = new Dictionary<string, DIContainer>(); | |
// this is used to support multiple containers. when instantiating from a container this is set so the monobehaviour DI can do its work | |
private static DIContainer activeContainer; | |
/// <summary> | |
/// The last used container for instantiation. This is set when instantiating prefabs. | |
/// </summary> | |
public static DIContainer ActiveContainer { get { return activeContainer; } } | |
/// <summary> | |
/// Get a DIContainer by name. This will create the container if it does not exist | |
/// </summary> | |
/// <param name="identifier">The name of the container</param> | |
/// <returns>The contianer</returns> | |
static public DIContainer GetContainer(string identifier) | |
{ | |
DIContainer container; | |
if (!containers.TryGetValue(identifier, out container)) | |
{ | |
container = Create(); | |
containers[identifier] = container; | |
} | |
return container; | |
} | |
// check for available configuration functions. These are marked with the DIConfiguration attribute | |
static DIContainer() | |
{ | |
// find static function with DIConfiguration attribute | |
List<MethodInfo> methods = new List<MethodInfo>(); | |
var assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |
foreach (var assembly in assemblies) | |
{ | |
// ignore default assemblies | |
if (assembly.FullName.StartsWith("System") || assembly.FullName.StartsWith("UnityEngine")) | |
continue; | |
Type[] types = new Type[0]; | |
try | |
{ | |
types = assembly.GetTypes(); | |
} | |
catch (ReflectionTypeLoadException e) | |
{ | |
// this can happen because of security limitations. e.types has the types it did manage to load, but might have null entries. | |
types = e.Types; | |
} | |
foreach (var type in types) | |
{ | |
if (type == null) | |
continue; | |
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) | |
{ | |
var attributes = method.GetCustomAttributes(typeof(DIConfigurationAttribute), false); | |
if (attributes.Length > 0) | |
methods.Add(method); | |
} | |
} | |
} | |
// execute all methods. If they return a DIContainer, then store it as the activeContainer. This is used for objects that are in the scene. | |
foreach (var method in methods) | |
{ | |
var result = method.Invoke(null, null) as DIContainer; | |
if (result != null) | |
activeContainer = result; | |
} | |
} | |
private DIContainer() | |
{ | |
// We add ourself as a service so other services can get a reference to the container they are in | |
services.Add(typeof(DIContainer), this); | |
} | |
private Dictionary<Type, object> services = new Dictionary<Type, object>(); | |
/// <summary> | |
/// Instantiate a prefab | |
/// </summary> | |
/// <typeparam name="T">The prefab type</typeparam> | |
/// <param name="prefab">The prefab to instantiate</param> | |
/// <param name="position">The position</param> | |
/// <param name="rotation">The rotation</param> | |
/// <returns>The newly instantiated object</returns> | |
public T Instantiate<T>(T prefab, Vector3 position, Quaternion rotation) where T: UnityEngine.Object | |
{ | |
activeContainer = this; | |
T result = (T)GameObject.Instantiate(prefab, position, rotation); | |
return result; | |
} | |
/// <summary> | |
/// Get a service from the container | |
/// </summary> | |
/// <param name="t">The service type</param> | |
/// <returns>The service if available or null otherwise</returns> | |
public object Get(Type t) | |
{ | |
object result; | |
services.TryGetValue(t, out result); | |
return result; | |
} | |
/// <summary> | |
/// Get a service from the container | |
/// </summary> | |
/// <typeparam name="T">The service type</typeparam> | |
/// <returns>The service if available or null otherwise</returns> | |
public T Get<T>() | |
{ | |
return (T)Get(typeof(T)); | |
} | |
/// <summary> | |
/// Register a service with the container | |
/// </summary> | |
/// <typeparam name="T">The type to bind to. The service given needs to be assignable to this type</typeparam> | |
/// <param name="service">The service object to bind</param> | |
/// <exception cref="ArgumentException">Thrown when the service can't be cast the to type it binds to</exception> | |
public void Register<T>(object service) | |
{ | |
if (!(service is T)) | |
{ | |
throw new ArgumentException("Service can't be cast to " + typeof(T).FullName); | |
} | |
object existing; | |
Type t = typeof(T); | |
services.TryGetValue(t, out existing); | |
if (existing != null) | |
{ | |
throw new AlreadyExistingException(); | |
} | |
services[t] = service; | |
} | |
internal void InjectProperties(object obj) | |
{ | |
var type = obj.GetType(); | |
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | |
foreach (var prop in props) | |
{ | |
var attr = prop.GetCustomAttributes(typeof(ServiceAttribute), true); | |
if (attr.Length > 0) | |
{ | |
var t = prop.PropertyType; | |
var s = this.Get(t); | |
prop.SetValue(obj, s, null); | |
} | |
} | |
} | |
/// <summary> | |
/// Create an instance of an object and inject services. If the object implements IService, then Init will be called on it. | |
/// </summary> | |
/// <typeparam name="T">The type to instantiate</typeparam> | |
/// <returns>The newly instantiated object</returns> | |
public T CreateInstance<T>() where T: new() | |
{ | |
var result = new T(); | |
InjectProperties(result); | |
IService service = result as IService; | |
if (service != null) | |
service.Init(); | |
return result; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection; | |
namespace UnityDI | |
{ | |
public class MonoBehaviourDI : ComponentDI.MonoBehaviourDI | |
{ | |
protected override void Awake() | |
{ | |
base.Awake(); | |
if (DIContainer.ActiveContainer == null) | |
return; | |
DIContainer.ActiveContainer.InjectProperties(this); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace UnityDI | |
{ | |
interface IService | |
{ | |
/// <summary> | |
/// Called after DI properties injection | |
/// </summary> | |
void Init(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace UnityDI | |
{ | |
class DIConfigurationAttribute : Attribute | |
{ | |
} | |
class ServiceAttribute : Attribute | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace UnityDI | |
{ | |
class AlreadyExistingException : Exception | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using BetterLogging; | |
using Events; | |
using log4net.Core; | |
using Superman.Game; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using UnityDI; | |
namespace Superman | |
{ | |
class Configuration | |
{ | |
[DIConfiguration] | |
static DIContainer DIConfig() | |
{ | |
DIContainer container = DIContainer.GetContainer("game"); | |
container.Register<EventManager>(new EventManager()); | |
container.Register<ObstacleService>(container.CreateInstance<ObstacleService>()); | |
container.Register<ObstacleSpawner>(container.CreateInstance<ObstacleSpawner>()); | |
container.Register<IBackgroundGenerator>(container.CreateInstance<ParkBackgroundGenerator>()); | |
container.Register<World>(container.CreateInstance<World>()); | |
return container; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using UnityDI; | |
using UnityEngine; | |
namespace Superman.Game | |
{ | |
class World | |
{ | |
private enum State | |
{ | |
Stopped, | |
Running | |
} | |
[Service] | |
ObstacleService ObstacleService { get; set; } | |
[Service] | |
ObstacleSpawner ObstacleSpawner { get; set; } | |
[Service] | |
IBackgroundGenerator BackgroundGenerator { get; set; } | |
public Vector2 ObstacleSpeed { get; set; } | |
private State state = State.Running; | |
public void Tick() | |
{ | |
if (state == State.Running) | |
{ | |
ObstacleService.MoveObstactles(ObstacleSpeed); | |
ObstacleSpawner.Tick(); | |
BackgroundGenerator.Tick(); | |
} | |
} | |
public void Pause() | |
{ | |
state = State.Stopped; | |
ObstacleService.Pause(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment