Unity游戏开发中的IoC依赖注入:模仿Springboot实现DI容器
Unity游戏开发中的IoC依赖注入:模仿Springboot实现DI容器
在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依赖注入的基本方法。这不仅能够提高代码的可维护性和可测试性,还能让开发过程更加愉快。