When I got interested in the ServiceLocator and started googling, one of the first things I hit upon is the fact, that it is an Anti Pattern and its use not encouraged.

With that said, I really missed a ServiceLocator in Unity (C#), as I am used to it from Symfony2 (PHP), so I did some digging, checked out existing examples and condensed it into what I think could be working for me and maybe for others as a general approach. So today, I want to put my solution up for discussion.

What I want to accomplish with my ServiceLocator is, that I want to keep the dependency injection out of my methods and ideally just declare my dependency once as an attribute, so that it looks somewhat like this:

class InputHandler
{
    readonly DefaultInputHandler inputService = ServiceLocator.Locate<DefaultInputHandler>();
    ...
    void Update()
    {
        inputService.Handle();
    }
}

The inputService.Handle() call should always retrieve the current service as of runtime state, in case I want to replace it at any time.
I don't care about what class is actually handling the input. That information should be hidden by the dependency injection and just give me whatever base interface I need.

If I were to retrieve the service as I did in the sample code, SomeClass would never know if I replaced the service at another point in the project code. Also, as the code is now, the Locate() would be called before I have the chance to initialize all the services I need, resulting in a null object and a NullPointerException.
As a result, I had to disjoin the actual service and the reference to the service.

So instead of using the InputHandler service directly, I put up a ServiceRef object in between. It stores the actual service object, similar to a list node element.

public class ServiceRef<T>
{
    public T service;

    public ServiceRef(T service = default(T))
    {
        this.service = service;
    }
}

The default(T) (which evaluates to null if no argument is passed) allows me to set up any reference to a service, before the concrete service is known. That way, every class with a dependency injection retrieves the service reference to the future service, which gets set at game start and potentially later when I want to replace the service.

The new dependency injection looks like this:

class InputHandler
{
    readonly ServiceRef<DefaultInputService> inputService = ServiceLocator.Locate<DefaultInputService>();
    ...
    void Update()
    {
        inputService.service.Handle();
    }
}

The service itself gets set in the Initialize() method of the ServiceLocator:

    public static void Initialize() {
        // Add all the services
#if UNITY_STANDALONE
        Set<DefaultInputService>(new StandaloneInputService());
#elif UNITY_ANDROID
        Set<DefaultInputService>(new AndroidInputService());
#endif
        ...
    }

The services are set-up as follows:

public interface IService
{
    void OnReplaceService(IService oldService);
}

////////

public class DefaultInputService : IService
{
    public virtual void OnKey(string s)
    {
        // Do Nothing?
    }

    public void OnReplaceService(IService oldService)
    {
        Debug.Log("Changing Service from " + (oldService == null ? "NULL" : oldService.GetType().ToString()) + " to " + this.GetType().ToString());
    }
}

////////

public class AndroidInputService : DefaultInputService
{
    public override void OnKey(String s)
    {
        // Android-specific input handling
    }
}

////////

public class StandaloneInputService : DefaultInputService
{
    public override void OnKey(string s)
    {
        // Win/Linux/Mac input handling
    }
}

The OnReplaceService call is bonus and might become handy, if you want to transfer some data between the service objects.
I found it handy and at least the debug code helps keeping track of your service changes.

And probably the most interesting class, the complete ServiceLocator:

public class ServiceLocator
{
    // Singleton
    static ServiceLocator Instance
    {
        get
        {
            if (_instance == null)
                _instance = new ServiceLocator();
            return _instance;
        }
    }
    static ServiceLocator _instance;

    private Dictionary<Type, object> registry = new Dictionary<Type, object>();

    private ServiceLocator()
    {
    }

    public static void Initialize() {
        // Add all the services
#if UNITY_STANDALONE
        Set<DefaultInputService>(new StandaloneInputService());
#elif UNITY_ANDROID
        Set<DefaultInputService>(new AndroidInputService());
#endif
        Debug.Log("ServiceLocator initialized!");
    }

    // Locate returns a service ref in any case.
    // If it doesn't exist yet, it gets created.
    // This is necessary to make dependency injections into
    // attributes work.
    public static ServiceRef<T> Locate<T>() where T : IService
    {
        try
        {
            object ret;
            if (Instance.registry.TryGetValue(typeof(T), out ret))
            {
                return (ServiceRef<T>)ret;
            }
        }
        catch
        {
        }
        // Service doesnt exist yet, return the reference
        ServiceRef<T> newRef = new ServiceRef<T>();
        Instance.registry.Add(typeof(T), newRef);
        return newRef;
    }

    public static T Set<T>(T newService) where T : IService
    {
        ServiceRef<T> curRef = Locate<T>();
        T oldService = curRef.service;
        curRef.service = newService;
        // Call the service replace hook if oldService is not null
        if (oldService != null)
            newService.OnReplaceService(oldService);
        return oldService;
    }
}

Services can be replaced at any time with the simple call:

ServiceLocator.Set<DefaultInputService>(new AndroidInputService());

and accessed in classes, where the dependency is injected with this:

inputService.service.OnKey("A");

The upsides for this are, that all the potentially slow code with typecasting and exception handling is in methods that are called only a couple of times, depending on how often you replace services.
The actual service dereferencing should not be a performance concern, since the ServiceRef typecast ensures that the service object doesn't have to be typecasted every time you dereference it through serviceRefVar.service (altough I could be wrong on this part).

A definitive Downside is the global scope, because dependency injection into attributes requires the ServiceLocator to be available at all times as a Singleton. The Initialize() method has to be called manually where it makes most sense, ideally from within a Start() method of an object that has a high Execution Priority.

I'd love to hear opinions about this, especially design and performance concerns I may not have covered.
Would you consider using this? Why / Why not?

mailto:radon@rn86.de