上篇文章讲到了基础的Ioc这篇文章我们将对其进行拓展方便我们框架的后期使用
框架:GitHub - DSHGFHDS/Cirilla: 适配HuaTuo、轻量、友好、易维护、可热更的Unity3d客户端框架x
感兴趣的小伙伴的可拉取一下。
下面开始对Ioc的拓展(老规矩本章不对Ioc进行抽象如果有小伙感兴趣可以自己尝试,丰富自己的动手能力)
//类型为 class ;接口为interface;实例为new过之后的类型
public class IocContainer
{
//单例,创建的方法大家不要模仿
public static IocContainer _ins=this;
//这个到我们用到在进行解释
private const BindingFlags Flag = BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.DeclaredOnly;
//进行了拓展,Type为父接口让我们的注册更加明了
//上篇注册主要进行注册他就存在当我们类型多了就会产生些许影响
//该篇对类型进行了分类,使我们寻找容器简单了许多(只需在自己的Type字典中)
private Dictionary<Type, Dictionary<string, ContentInfo>> stock;
//构造
public IocContainer() => stock = new Dictionary<Type, Dictionary<string, ContentInfo>>();
/// <summary>
/// 注册方法
/// </summary>
/// <typeparam name="T">父接口</typeparam>
/// <param name="type">注入的类型</param>
/// <param name="key">该类型的名称</param>
public void Register<T>(Type type, string key) =>
Register<T>(key, new ContentInfo(type, key, null));
/// <summary>
///对该类型进行注入
/// </summary>
/// <typeparam name="T">Type需要注册的类型</typeparam>
/// <param name="key"></param>
/// <param name="contentInfo"></param>
private void Register<T>(string key, ContentInfo contentInfo)
{
Type type = typeof(T);
if (stock.TryGetValue(type,
out Dictionary<string, ContentInfo> contentInfos))
{
//判断容器是否存在同一个类型
if (contentInfos.ContainsKey(key))
return;
contentInfos.Add(key, contentInfo);
return;
}
//对容器进行存储
stock.Add(type,
new Dictionary<string, ContentInfo>() { { key, contentInfo } });
}
}
/// <summary>
/// 既然把类型注入到容器中则需要使用,该Api为获取实例
/// </summary>
/// <typeparam name="T">Type(需要获取实例的父接口)</typeparam>
/// <param name="key">该类型注册时的名称</param>
/// <returns></returns>
public object Resolve(Type type, string key)
{
if (!stock.TryGetValue(type,
out Dictionary<string, ContentInfo> contentInfos))
{
Debug.Log("类型未注册:" + type);
return null;
}
if (!contentInfos.TryGetValue(key, out ContentInfo contentInfo))
{
Debug.Log("键值未注册:" + key);
return null;
}
object obj = contentInfo.obj;
if (contentInfo.IsInjected)
return obj;
//对该类型的标记字段进行实例化
Inject(contentInfo);
contentInfo.IsInjected = true;
return obj;
}
/// <summary>
/// 对Type的字段(被特性标记过的字段)进行实例获取
/// </summary>
/// <param name="contentInfo"></param>
private void Inject(ContentInfo contentInfo)
{
//先获取实例
Object obj = contentInfo.obj;
//需要用到反射所以获取类型
Type type = obj.GetType();
//获取type中的字段属性()
//通过反射获取全部到字段Flag被使用
FieldInfo[] fieldInfo = type.GetFields(Flag);
for (int i = 0; i < fieldInfo.Length; i++)
{
//对字段进行保存
contentInfo.fieldInfos.Add(fieldInfo[i]);
//通过获取标记若该字段被标记则进行下一步,反之下一关循环
DependencyAttribute attribute =
fieldInfo[i].GetCustomAttribute<DependencyAttribute>();
if (attribute == null)
continue;
//获取该字段的类型
Type fieldType = fieldInfo[i].FieldType;
//调用获取实例的方法
object target = Resolve(fieldType, attribute.key);
//需要该类型被注册到容器才获取得到
if(target == null)
{
debug.Log("未注册:" + type.Name);
continue;
}
//对该实例的字段进行赋值
fieldInfo[i].SetValue(obj, target);
}
}
下面是容器ContentInfo的拓展
public class ContentInfo
{
//注册的类型
public Type type { get; private set; }
//注册的名字
public string key { get; private set; }
//该类型的标记字段
public List<FieldInfo> fieldInfos;
//该容器是否已经经过IocContainer.Inject
public bool IsInjected { get; set; }
//创建实例
public object obj { get { return ins ?? (ins = Activator.CreateInstance(type)); } }
private object ins { get; set; }
//构造
public ContentInfo(Type type, string key)
{
this.type = type;
this.key = key;
this.ins = ins;
}
}
下面是标签(特性)
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class DependencyAttribute : Attribute
{
public string key { get; private set; }
public DependencyAttribute(string key = ""){
this.key = key;
}
}
使用:
private void Start()
{
IocContainer container=IocContainer._ins;
//这样就注册进去了一个Type,
container.Register<父接口, 子类型>(子类型.tostring());
container.Register<功能父接口, 功能类型>(功能类型.tostring());
//当子类型被获取实例后功能类自动被获取实例
子类型 xxx=container.Resolve(接口,子类型.tostring());
}
class 子类型:父接口
{
[Dependency("功能类型")] 功能类 xxx;
}
其实对Ioc还可以继续拓展还就是获取当容器,以及移除容器;这些由各位小伙伴自行拓展。
如有问题,或是更好的想法欢迎留言!!!!