using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using EasyInject.Attributes;
using EasyInject.Behaviours;
using EasyInject.Models;
using UnityEngine;
using Object = UnityEngine.Object;
namespace EasyInject.Utils
{
///
/// author: spyn
/// description: IoC容器
///
public class MyIoC : IIoC
{
// IoC容器
private readonly Dictionary _beans = new();
// 尚未注入字段的实例
private readonly Dictionary> _shelvedInstances = new();
public MyIoC()
{
// 注册自己
RegisterTypeAndParentsAndInterfaces(string.Empty, this, GetType());
// 注册非游戏物体Bean(一般会持续保留到程序结束)
InitNormalBean();
}
///
/// 创建一个GameObject作为Bean
///
/// 原型
/// 名字
/// Bean类型
/// Bean实例
public T CreateGameObjectAsBean(GameObject original, string beanName) where T : MonoBehaviour
{
var go = Object.Instantiate(original);
return CheckNewGameObj(go, beanName);
}
///
/// 创建一个GameObject作为Bean
///
/// 原型
/// 名字
/// 父物体
/// Bean类型
/// Bean实例
public T CreateGameObjectAsBean(GameObject original, string beanName, Transform parent)
where T : MonoBehaviour
{
var go = Object.Instantiate(original, parent);
return CheckNewGameObj(go, beanName);
}
///
/// 创建一个GameObject作为Bean
///
/// 原型
/// 名字
/// 父物体
/// 是否在世界空间中实例化
/// Bean类型
/// Bean实例
public T CreateGameObjectAsBean(GameObject original, string beanName, Transform parent,
bool instantiateInWorldSpace) where T : MonoBehaviour
{
var go = Object.Instantiate(original, parent, instantiateInWorldSpace);
return CheckNewGameObj(go, beanName);
}
///
/// 创建一个GameObject作为Bean
///
/// 原型
/// 名字
/// 位置
/// 旋转
/// Bean类型
/// Bean实例
public T CreateGameObjectAsBean(GameObject original, string beanName, Vector3 position, Quaternion rotation)
where T : MonoBehaviour
{
var go = Object.Instantiate(original, position, rotation);
return CheckNewGameObj(go, beanName);
}
///
/// 创建一个GameObject作为Bean
///
/// 原型
/// 名字
/// 位置
/// 旋转
/// 父物体
/// Bean类型
/// Bean实例
public T CreateGameObjectAsBean(GameObject original, string beanName, Vector3 position, Quaternion rotation,
Transform parent) where T : MonoBehaviour
{
var go = Object.Instantiate(original, position, rotation, parent);
return CheckNewGameObj(go, beanName);
}
///
/// 检查新物体是否符合创建Bean的条件
///
/// 游戏物体
/// Bean名字
/// Bean类型
/// Bean实例
/// 如果没有找到对应的组件
private T CheckNewGameObj(GameObject go, string beanName) where T : MonoBehaviour
{
// 获得当前场景的名字
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
// 如果T是BeanObject或AcrossScenesBeanObject且没有挂载,就挂载BeanObject或AcrossScenesBeanObject,否则报错
var behaviour = go.GetComponent();
if (behaviour == null)
{
if (typeof(T) == typeof(AcrossScenesBeanObject))
{
behaviour = go.AddComponent() as T;
}
else if (typeof(T) == typeof(BeanObject))
{
behaviour = go.AddComponent() as T;
}
else
{
throw new Exception($"Did not find the corresponding component as {typeof(T)}");
}
}
// 如果在IoC容器中已经有这个Bean,就拋出异常
if (_beans.ContainsKey(new BeanInfo(beanName, typeof(T))))
{
throw new Exception($"Bean {beanName} already exists");
}
AddBean(beanName, behaviour, scene);
return behaviour;
}
///
/// 删除一个游戏物体Bean
///
/// Bean实例
/// Bean名字
/// 是否删除游戏物体
/// 延迟时间
/// Bean类型
/// 是否删除成功
public bool DeleteGameObjBean(T bean, string beanName = "", bool deleteGameObj = false, float t = 0.0F)
where T : MonoBehaviour
{
if (bean == null) return false;
var beanInfo = new BeanInfo(beanName, bean.GetType());
if (!_beans.ContainsKey(beanInfo)) return false;
_beans.Remove(beanInfo);
if (!deleteGameObj)
{
// 如果不需要删除游戏物体,就只删除Bean
Object.Destroy(bean, t);
}
else
{
// 查找游戏物体上有没有其他的Bean组件(包括打了GameObjectBean特性的组件,以及继承了BeanObject的组件)
var otherBeans = bean.GetComponents().Where(monoBehaviour =>
monoBehaviour.GetType().GetCustomAttribute() != null ||
monoBehaviour.GetType().IsSubclassOf(typeof(BeanObject))).ToList();
foreach (var info in otherBeans.Select(beans => new BeanInfo(beans.name, beans.GetType()))
.Where(info => _beans.ContainsKey(info)))
{
_beans.Remove(info);
}
Object.Destroy(bean.gameObject, t);
}
return true;
}
///
/// 立即删除一个游戏物体Bean
///
/// Bean实例
/// Bean名字
/// 是否删除游戏物体
/// Bean类型
/// 是否删除成功
public bool DeleteGameObjBeanImmediate(T bean, string beanName = "", bool deleteGameObj = false)
where T : MonoBehaviour
{
if (bean == null) return false;
var beanInfo = new BeanInfo(beanName, bean.GetType());
if (!_beans.ContainsKey(beanInfo)) return false;
_beans.Remove(beanInfo);
if (!deleteGameObj)
{
// 如果不需要删除游戏物体,就只删除Bean
Object.DestroyImmediate(bean);
}
else
{
// 查找游戏物体上有没有其他的Bean组件(包括打了GameObjectBean特性的组件,以及继承了BeanObject的组件)
var otherBeans = bean.GetComponents().Where(monoBehaviour =>
monoBehaviour.GetType().GetCustomAttribute() != null ||
monoBehaviour.GetType().IsSubclassOf(typeof(BeanObject))).ToList();
foreach (var info in otherBeans.Select(beans => new BeanInfo(beans.name, beans.GetType()))
.Where(info => _beans.ContainsKey(info)))
{
_beans.Remove(info);
}
Object.DestroyImmediate(bean.gameObject);
}
return true;
}
///
/// 获取一个Bean
///
/// Bean的名字
/// Bean的类型
/// Bean实例
public T GetBean(string name = "") where T : class
{
var beanInfo = new BeanInfo(name, typeof(T));
if (_beans.TryGetValue(beanInfo, out var value))
{
return (T) value;
}
return null;
}
public List GetBeans() where T : class
{
List < T > list = new List < T >();
foreach (var (k,v) in _beans)
{
if (v is T obj)
{
list.Add(obj);
}
}
return list;
}
///
/// 清空该场景的Bean
///
/// 场景名称
/// 是否清空跨场景的Bean
public void ClearBeans(string scene = null, bool clearAcrossScenesBeans = false)
{
scene ??= UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
foreach (var (beanInfo, value) in _beans.Where(bean => bean.Key.Scenes.Contains(scene)).ToList())
{
// 如果要清空跨场景的Bean,要删除跨场景Bean的GameObject
if (value.GetType().GetCustomAttribute() != null)
{
if (!clearAcrossScenesBeans)
continue;
DeleteGameObjBeanImmediate(value as MonoBehaviour, beanInfo.Name, true);
}
else
{
_beans.Remove(beanInfo);
}
}
_shelvedInstances.Clear();
}
///
/// 清空该场景的Bean
///
/// 是否清空跨场景的Bean
public void ClearBeans(bool clearAcrossScenesBeans)
{
ClearBeans(null, clearAcrossScenesBeans);
}
///
/// 获取场景中需要注入的MonoBehaviour实例
///
public void Init()
{
// 获得当前场景的名字
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
// 获得场景上所有挂载了BeanObject的物体
var beanObjects = Resources.FindObjectsOfTypeAll()
// 非运行时环境下,如果预制体也在场景中,会把场景中物件和预制体都找到导致重复,因此必须筛选出来
#if UNITY_EDITOR
.Where(beanObject => !UnityEditor.PrefabUtility.IsPartOfPrefabAsset(beanObject) &&
!UnityEditor.PrefabUtility.IsPartOfPrefabInstance(beanObject))
#endif
.ToList();
foreach (var beanObject in beanObjects)
{
if (_beans.ContainsKey(new BeanInfo(beanObject.name, typeof(BeanObject))))
{
// 检查是不是PersistAcrossScenesAttribute,如果是就往场景列表中添加场景
if (beanObject.GetType().GetCustomAttribute() != null)
{
var keys = _beans.Where(bean =>
bean.Key.Name == beanObject.name && bean.Value.Equals(beanObject))
.Select(bean => bean.Key).ToList();
foreach (var key in keys.Where(key => !key.Scenes.Contains(scene)))
{
key.Scenes.Add(scene);
}
}
continue;
}
// 不需要进行字段依赖注入,因为BeanObject本来就是空的
AddBean(beanObject.name, beanObject, scene, false);
}
// 获得场景上所有挂载了GameObjectBeanAttribute的物体
var gameObjectBeans = Resources.FindObjectsOfTypeAll().Where(monoBehaviour =>
monoBehaviour.GetType().GetCustomAttribute() != null)
// 非运行时环境下,如果预制体也在场景中,会把场景中物件和预制体都找到导致重复,因此必须筛选出来
#if UNITY_EDITOR
.Where(monoBehaviour => !UnityEditor.PrefabUtility.IsPartOfPrefabAsset(monoBehaviour) &&
!UnityEditor.PrefabUtility.IsPartOfPrefabInstance(monoBehaviour))
#endif
.ToList();
foreach (var gameObjectBean in gameObjectBeans)
{
var attribute = gameObjectBean.GetType().GetCustomAttribute();
// 先获取NameType,再根据NameType获取名字
var name = attribute.NameType switch
{
ENameType.Custom => attribute.Name,
ENameType.ClassName => gameObjectBean.GetType().Name,
ENameType.GameObjectName => gameObjectBean.name,
// 筛选出有BeanName特性的字段,然后获取字段的值
ENameType.FieldValue => gameObjectBean
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance)
.Where(field => field.GetCustomAttribute() != null)
.Select(field => field.GetValue(gameObjectBean).ToString())
.FirstOrDefault() ??
string.Empty,
_ => string.Empty
};
if (_beans.ContainsKey(new BeanInfo(name, gameObjectBean.GetType())))
{
// 检查是不是PersistAcrossScenesAttribute,如果是就往场景列表中添加场景
if (gameObjectBean.GetType().GetCustomAttribute() != null)
{
var keys = _beans.Where(bean => bean.Key.Name == name && bean.Value.Equals(gameObjectBean))
.Select(bean => bean.Key).ToList();
foreach (var key in keys.Where(key => !key.Scenes.Contains(scene)))
{
key.Scenes.Add(scene);
}
}
continue;
}
AddBean(name, gameObjectBean, scene);
}
}
///
/// 注入非MonoBehavior实例
///
private void InitNormalBean()
{
// 扫描所有程序集中打了Component特性的类
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.GetCustomAttributes(typeof(ComponentAttribute), true).Length > 0).ToList();
// 先进行实例化(包括构造函数的依赖注入)
while (types.Count > 0)
{
for (var i = 0; i < types.Count; i++)
{
var type = types[i];
// 获取构造函数
var constructors = type.GetConstructors();
// 实例
object instance = null;
// 遍历构造函数,找到可以实例化的构造函数
foreach (var constructor in constructors)
{
// 获取构造函数的参数
var parameters = constructor.GetParameters();
// 构造函数的参数实例
var parameterInstances = new object[parameters.Length];
for (var j = 0; j < parameters.Length; j++)
{
// 获取参数的类型
var parameterType = parameters[j].ParameterType;
// 获取参数上Autowired特性,也可以不打,视为Name为Empty
var name = parameters[j].GetCustomAttribute()?.Name;
var beanInfo = new BeanInfo(name, parameterType);
// 如果IoC容器中有这个参数的实例,就注入
if (_beans.TryGetValue(beanInfo, out var parameterInstance))
{
parameterInstances[j] = parameterInstance;
}
else
{
break;
}
}
// 如果有参数没有实例化,就跳过这个构造函数
if (parameterInstances.Contains(null)) continue;
instance = constructor.Invoke(parameterInstances);
break;
}
// 如果没有找到可以实例化的构造函数,就找无参构造函数
if (instance == null && type.GetConstructor(Type.EmptyTypes) != null)
{
instance = Activator.CreateInstance(type);
}
if (instance == null) continue;
RegisterTypeAndParentsAndInterfaces(
type.GetCustomAttribute()?.Name ?? string.Empty, instance, type);
// 从待注册列表中移除
types.RemoveAt(i);
i--;
}
}
// 开始进行字段和属性的依赖注入
foreach (var type in _beans.Keys.ToList())
{
var instance = _beans[type];
// 如果有PersistAcrossScenesAttribute特性,跳过,因为这个是持久化的游戏组件对象
if (instance.GetType().GetCustomAttribute() != null)
{
continue;
}
// 获得打了Autowired特性的字段
var fields = instance.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0).ToList();
var injected = new HashSet();
foreach (var field in fields)
{
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = field.GetCustomAttribute()?.Name;
var beanInfo = new BeanInfo(name, field.FieldType);
// 如果IoC容器中有这个类型的实例,就注入
if (_beans.TryGetValue(beanInfo, out var value))
{
field.SetValue(instance, value);
injected.Add(field.Name);
}
else
{
ShelvedInstance shelvedInstance = new ShelvedInstance(type.Name, instance);
if (_shelvedInstances.ContainsKey(shelvedInstance))
{
Debug.LogWarning("重复的bean ="+type.Name +" 实例 ="+instance);
_shelvedInstances[shelvedInstance] = injected;
return;
}
_shelvedInstances.Add(shelvedInstance, injected);
}
}
// 获得打了Autowired特性的属性
var properties = instance.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0).ToList();
foreach (var property in properties)
{
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = property.GetCustomAttribute()?.Name;
var beanInfo = new BeanInfo(name, property.PropertyType);
// 如果IoC容器中有这个类型的实例,就注入
if (_beans.TryGetValue(beanInfo, out var value))
{
property.SetValue(instance, value);
injected.Add(property.Name);
}
else
{
_shelvedInstances.Add(new ShelvedInstance(type.Name, instance), injected);
}
}
}
}
///
/// 这个方法一般用于传入MonoBehaviour实例,然后进行字段的依赖注入
///
///
/// 要注意如果暂时无法完成注入,会被搁置,如果一直被搁置证明没有生成对应的Bean,在外部调用时肯定会报错
///
/// Bean的名字
/// MonoBehaviour实例
private void Inject(string beanName, object instance)
{
var type = instance.GetType();
// 字段注入
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0);
var injected = new HashSet();
foreach (var field in fields)
{
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = field.GetCustomAttribute()?.Name;
var beanInfo = new BeanInfo(name, field.FieldType);
// 如果IoC容器中有这个类型的实例,就注入
if (_beans.TryGetValue(beanInfo, out var value))
{
field.SetValue(instance, value);
injected.Add(field.Name);
}
else
{
var insKey = new ShelvedInstance(beanName, instance);
// 暂时放入_shelvedInstances中,等到有实例的时候再注入
_shelvedInstances.Add(insKey, injected);
break;
}
}
// 属性注入
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0);
foreach (var property in properties)
{
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = property.GetCustomAttribute()?.Name;
var beanInfo = new BeanInfo(name, property.PropertyType);
// 如果IoC容器中有这个类型的实例,就注入
if (_beans.TryGetValue(beanInfo, out var value))
{
property.SetValue(instance, value);
injected.Add(property.Name);
}
else
{
var insKey = new ShelvedInstance(beanName, instance);
// 暂时放入_shelvedInstances中,等到有实例的时候再注入
_shelvedInstances.Add(insKey, injected);
break;
}
}
CheckShelvedInstances();
}
///
/// 新增一个Bean(一般是MonoBehaviour)
///
///
/// 要注意如果暂时无法完成注入,会被搁置,如果一直被搁置证明没有生成对应的Bean,在外部调用时肯定会报错
///
/// Bean的名字
/// Bean的实例
/// 场景名称
/// 是否立即注入
private void AddBean(string name, T instance, string scene, bool startInject = true)
where T : MonoBehaviour
{
RegisterTypeAndParentsAndInterfaces(name, instance, instance.GetType(), scene);
if (startInject)
{
Inject(name, instance);
}
}
///
/// 检查_shelvedInstances中的实例是否可以注入
///
private void CheckShelvedInstances()
{
foreach (var (key, injected) in _shelvedInstances.ToList())
{
var insFields = key.Instance.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0).ToList();
var insProperties = key.Instance.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.GetCustomAttributes(typeof(AutowiredAttribute), true).Length > 0).ToList();
var count = insFields.Count + insProperties.Count;
foreach (var field in insFields)
{
// 如果已经注入过了,就跳过
if (injected.Contains(field.Name))
{
count--;
continue;
}
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = field.GetCustomAttribute().Name;
var beanInfo = new BeanInfo(name, field.FieldType);
// 如果IoC容器中有这个类型的实例,就注入
if (!_beans.TryGetValue(beanInfo, out var value))
{
continue;
}
field.SetValue(key.Instance, value);
injected.Add(field.Name);
count--;
}
foreach (var property in insProperties)
{
// 如果已经注入过了,就跳过
if (injected.Contains(property.Name))
{
count--;
continue;
}
// 如果Autowired传了名字参数,名字和类型都要匹配
var name = property.GetCustomAttribute().Name;
var beanInfo = new BeanInfo(name, property.PropertyType);
// 如果IoC容器中有这个类型的实例,就注入
if (!_beans.TryGetValue(beanInfo, out var value))
{
continue;
}
property.SetValue(key.Instance, value);
injected.Add(property.Name);
count--;
}
if (count == 0)
{
_shelvedInstances.Remove(key);
}
}
}
///
/// 按照继承链注册自己、父类和接口
///
/// bean的名称
/// bean的实例
/// bean的类型
/// 场景名称
private void RegisterTypeAndParentsAndInterfaces(string name, object instance, Type type, string scene = "")
{
name ??= string.Empty;
// 注册自己
var beanInfo = new BeanInfo(name, type, scene);
if (!_beans.TryAdd(beanInfo, instance))
{
throw new Exception($"Bean {name} already exists");
}
// 注册父类
var baseType = type.BaseType;
if (baseType != null && baseType != typeof(object) && baseType.Namespace != null &&
!baseType.Namespace.Contains("UnityEngine"))
{
RegisterTypeAndParentsAndInterfaces(name, instance, baseType, scene);
}
// 注册接口
var interfaces = type.GetInterfaces();
foreach (var @interface in interfaces)
{
if (@interface.Namespace == null || @interface.Namespace.Contains("UnityEngine")) continue;
RegisterTypeAndParentsAndInterfaces(name, instance, @interface, scene);
}
}
public bool AddBean(T bean, string beanName = "") where T : MonoBehaviour
{
// 获得当前场景的名字
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
AddBean(beanName,bean,scene);
return true;
}
}
}