从类型和实例的字典中获取实例

时间:2017-04-21 14:33:28

标签: c# dictionary generics unity3d types

我有一个名为ServiceManager的简单服务管理器,它有两种方法。 Create()创建服务实例。 Provide()返回先前已创建的服务。

我有一个基本的实现,但我想知道是否有更清洁的方式。这是我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
    }
}

使用该服务很简单:

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
    }
}

我的问题是ServiceManager.Provide()。从字典中获取项目后,请注意转换为T.这种感觉非常不干净,让我想知道我是否遗漏了一些关于泛型如何在C#中工作的东西。是否有其他/更好的方法来完成我想要完成的任务?

2 个答案:

答案 0 :(得分:3)

这里没有什么可以改进的。强制转换是必要的,因为字典值类型是MonoBehaviour知道它实际上是T,但编译器并没有。你必须通过施放来告诉你。

你做得很好。

答案 1 :(得分:1)

如果每种类型只有一个实例,那就更好了。考虑一个静态泛型类型

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
    }
}

使用该服务很简单:

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
    }
}

此外,如果您有多个ServiceManagers,那么这不会起作用。