使用反射加 xml 或者特性解析实现
二,实现1,xml 实现
- class 代码
// User 类namespace CsharpIOC{public class User{public string name { get; set; }public string age { get; set; } } }复制代码
- Xml容器类
public class XmlApplicationContext{/// <summary>/// bean 容器/// </summary>private static Dictionary<string, object> _beanContainer = new Dictionary<string, object>();public XmlApplicationContext(string xmlPath){ XmlDocument xmlDoc = new XmlDocument();//读取 xml 文件xmlDoc.Load(xmlPath);//获取 xml 文件中所有的 bean 节点XmlNodeList nodeList = xmlDoc.SelectNodes("Bean");foreach (XmlNode node in nodeList) {//bean idstring beanId = string.Empty;//bean 的全类名,命名空间+类名string beanClass = string.Empty;foreach (XmlNode child in node.Attributes) {if (child.Name.Equals("id")) { beanId = child.Value; }else if (child.Name.Equals("class")) { beanClass = child.Value; } }//通过反射创建类的实例Type beanType = Type.GetType(beanClass);object bean = Activator.CreateInstance(beanType);//接下来调用set方法注入属性foreach (XmlNode child in node.ChildNodes) {//获取该类的所有方法var methods = beanType.GetMethods();foreach (var method in methods) {//默认的 set 方法名是 set_属性名,例如 set_nameif (method.Name.Equals($"set_{child.Name}")) {//调用 set 方法注入属性method.Invoke(bean,new object[] { child.InnerText}); } } }//添加 bean 到容器中_beanContainer[beanId] = bean; } }/// <summary>/// 通过 bean id 获取 bean 实例/// </summary>/// <param name="beanId"></param>/// <returns></returns>public object getBean(string beanId) => _beanContainer[beanId]; }复制代码
- xml 配置
<?xml version="1.0" encoding="utf-8" ?><Bean id ="user" class="CsharpIOC.User"> <name>任我行</name> <age>45</age></Bean>复制代码
- 测试
XmlApplicationContext ctx = new XmlApplicationContext("Bean.xml"); User user = (User)ctx.getBean("user"); Console.WriteLine(user.name); Console.WriteLine(user.age);复制代码
2,特性实现
- 特性准备
/// <summary>/// 标记类可被容器扫描/// </summary>public class Component : Attribute{public string id { get; set; } }/// <summary>/// 标记字段的注入值/// </summary>public class Value : Attribute{public object value { get; set; } }复制代码
- class
namespace CsharpIOC{ [Component(id = "user")]public class User{ [Value(value = "东方不败")]public string name { get; set; } [Value(value = "99")]public string age { get; set; } } }复制代码
- 特性容器类
public class AttributeaApplicationContext{/// <summary>/// bean 容器/// </summary>private static Dictionary<string, object> _beanContainer = new Dictionary<string, object>();public AttributeaApplicationContext(string nameSpace){//加载命名空间Assembly asm = Assembly.Load(nameSpace);//遍历命名空间下的类foreach (Type type in asm.DefinedTypes) {//找出带有 Componment 自定义特性标记的类var attribute = type.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Equals(typeof(Component)));if (attribute != null) {//读取 Componment 中的 bean idstring beanId = attribute.NamedArguments.FirstOrDefault(na => na.MemberName.Equals("id")).TypedValue.Value.ToString();//实例化该 bean 对象object bean = Activator.CreateInstance(type);var properties = type.GetProperties();//遍历该 bean 的属性foreach (var property in properties) {//找出带有 Value 自定义特性标记的 bean 属性var proAttribute = property.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Equals(typeof(Value)));if (proAttribute != null) {//获取 Value 特性的值并注入var kv = proAttribute.NamedArguments.FirstOrDefault(na => na.MemberName.Equals("value"));var method = type.GetMethods().FirstOrDefault(m => m.Name.Equals($"set_{property.Name}"));if (method != null && kv != null) { method.Invoke(bean,new object[] { kv.TypedValue.Value}); } } }//添加 bean 到容器中_beanContainer[beanId] = bean; } } }/// <summary>/// 通过 bean id 获取 bean 实例/// </summary>/// <param name="beanId"></param>/// <returns></returns>public object getBean(string beanId) => _beanContainer[beanId]; }复制代码
- 测试
AttributeaApplicationContext ctx = new AttributeaApplicationContext("CsharpIOC"); User user = (User)ctx.getBean("user"); Console.WriteLine(user.name); Console.WriteLine(user.age);复制代码三,结语
其实大部分已知的框架都是使用反射和设计模式组合实现的,IOC的核心便是反射,本文只是大概讲述其原理,实际其中还有很多的细节是没有讲到的,比如调用set方法时的参数类型转换,复杂的内嵌属性注入等等。