I have a simple service manager called ServiceManager
that has two methods. Create()
creates an instance of a service. Provide()
returns a service that has previously been created.
I have a basic implementation that works but am wondering if there's a cleaner way. This is my basic implementation of the ServiceManager
:
public class ServiceManager : MonoBehaviour
{
private Dictionary<Type, MonoBehaviour> services = new Dictionary<Type, MonoBehaviour>();
public void Create<T>() where T : MonoBehaviour
{
// Create service
GameObject serviceObject = new GameObject(typeof(T).Name);
serviceObject.transform.SetParent(transform); // make service GO our child
T service = serviceObject.AddComponent<T>(); // attach service to GO
// Register service
services.Add(typeof(T), service);
}
public T Provide<T>() where T : MonoBehaviour
{
return (T)services[typeof(T)]; // notice the cast to T here
}
}
Using the service is simple:
public class ServiceTest : MonoBehaviour
{
private void Start()
{
// Creating services
ServiceManager services = FindObjectOfType<ServiceManager>();
services.Create<MapService>();
services.Create<InteractionService>();
}
private void Example()
{
// Get a service
ServiceManager services = FindObjectOfType<ServiceManager>();
MapService map = services.Provide<MapService>();
// do whatever you want with map
}
}
My question is about ServiceManager.Provide()
. Notice the cast to T after getting the item from the dictionary. This feel very unclean and makes me wonder if I am missing something about how generics work in C#. Are there other/better ways to do what I am trying to accomplish?
There is nothing to improve here. The cast is necessary because the dictionary value type is a MonoBehaviour
. You know that it is actually T
, but the compiler doesn't. You have to tell that by casting.
You did well.
If there is only ever one instance per type, then there is better. Consider a static generic type
using UnityEngine;
public class ServiceManager : MonoBehaviour
{
// If this T confuses you from the generic T used elsewhere, rename it
public static Transform T { get; private set; }
void Awake()
{
T = transform;
}
public T Provide<T>() where T : MonoBehaviour
{
return ServiceMap<T>.service; // no cast required
}
}
static class ServiceMap<T> where T : MonoBehaviour
{
public static readonly T service;
static ServiceMap()
{
// Create service
GameObject serviceObject = new GameObject(typeof(T).Name);
serviceObject.transform.SetParent(ServiceManager.T); // make service GO our child
service = serviceObject.AddComponent<T>(); // attach service to GO
}
}
Using the service is simple:
public class ServiceTest : MonoBehaviour
{
private void Start()
{
// no need to Create services
// They will be created when Provide is first called on them
// Though if you want them up and running at Start, call Provide
// on each here.
}
private void Example()
{
// Get a service
ServiceManager services = FindObjectOfType<ServiceManager>();
MapService map = services.Provide<MapService>();
// do whatever you want with map
}
}
Also, if you have multiple ServiceManagers
then this won't work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With