一,原理
使用反射加 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 id
string 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_name
if (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 id
string 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方法时的参数类型转换,复杂的内嵌属性注入等等。