问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity游戏开发中的IoC依赖注入:模仿Springboot实现DI容器

创作时间:
作者:
@小白创作中心

Unity游戏开发中的IoC依赖注入:模仿Springboot实现DI容器

引用
1
来源
1.
https://www.bilibili.com/read/mobile?id=33866225

在Unity游戏开发中,如何实现类似于Springboot的IoC依赖注入?本文将详细介绍如何在C#项目中(特别是Unity游戏)模仿Springboot编写IoC依赖注入容器。

非Unity环境下的IoC容器实现

首先,创建一个解决方案,在解决方案下方建立一个控制台项目ConsoleApp1和一个类库项目EasyInject,分别用来进行依赖注入测试和编写IoC容器。

在EasyInject/Attributes下,建立两个特性类,分别叫AutowiredAttribute和ComponentAttribute,前者用于打在成员变量字段前进行字段注入,后者用于标记这个类应该被视作类似于Bean的东西记录在IoC容器当中。

接下来编写IoC容器的核心代码:

public class MyIoC
{
    private Dictionary<Type, object> beans = new Dictionary<Type, object>();

    public MyIoC()
    {
        var types = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(s => s.GetTypes())
            .Where(p => p.IsClass && p.GetCustomAttributes(typeof(ComponentAttribute), false).Length > 0);

        while (types.Any())
        {
            var type = types.First();
            var constructors = type.GetConstructors();
            var constructor = constructors.FirstOrDefault(c => c.GetParameters().Length == 0);
            if (constructor != null)
            {
                var instance = constructor.Invoke(null);
                beans.Add(type, instance);
                types = types.Where(t => t != type);
            }
            else
            {
                foreach (var c in constructors)
                {
                    var parameters = c.GetParameters();
                    var canInject = true;
                    foreach (var p in parameters)
                    {
                        if (!beans.ContainsKey(p.ParameterType))
                        {
                            canInject = false;
                            break;
                        }
                    }
                    if (canInject)
                    {
                        var instance = c.Invoke(parameters.Select(p => beans[p.ParameterType]).ToArray());
                        beans.Add(type, instance);
                        types = types.Where(t => t != type);
                        break;
                    }
                }
            }
        }

        foreach (var key in beans.Keys)
        {
            var fields = key.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (var field in fields)
            {
                if (field.GetCustomAttributes(typeof(AutowiredAttribute), false).Length > 0)
                {
                    var fieldType = field.FieldType;
                    if (beans.ContainsKey(fieldType))
                    {
                        field.SetValue(beans[key], beans[fieldType]);
                    }
                }
            }
        }
    }

    public object GetBean(Type type)
    {
        return beans[type];
    }
}

在Unity中的应用

将EasyInject项目代码移动到Unity项目当中,特性类放在了Assets/Scripts/Attributes下,IoC容器放在了Assets/Scripts/Utils下。

由于Unity项目没有向我们提供C#启动入口类,所以需要我们自己编写一个脚本。可以在脚本当中保留一个IoC容器的实例,然后利用Unity提供的特性,编写一个方法实例化IoC容器并在场景渲染前执行。

对于非游戏物体组件脚本来说可以打Component特性,将其作为Bean存入字典中,但组件类是绝对不可以这么做,特别是通过构造器进行依赖注入的方法肯定是不行的。因此,可以使用字段注入的方式。

修改MyIoC的代码,删除GetBean方法,添加如下方法:

public void RegisterBean(object bean)
{
    var type = bean.GetType();
    beans.Add(type, bean);
    var interfaces = type.GetInterfaces();
    foreach (var @interface in interfaces)
    {
        beans.Add(@interface, bean);
    }
}

编写一个抽象类InjectableMonoBehaviour,继承自MonoBehaviour,然后在Start生命周期钩子当中注册自己,有需要依赖注入的类就继承这个抽象类。

在控制类当中,继承InjectableMonoBehavior类,然后添加service接口类的成员变量。调用服务也得像这样调用:

运行游戏,可以获取到后端数据。

进一步扩展

在Unity中,还可以将UI和游戏内一些单例物件通过依赖注入的方式进行解耦。这需要对IoC容器进行进一步的扩展,例如给同类型的Bean设置不同的名称进行依赖注入。

总结

通过本文的介绍,读者可以掌握在Unity中实现IoC依赖注入的基本方法。这不仅能够提高代码的可维护性和可测试性,还能让开发过程更加愉快。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号