Reflection & DI - Dependency Injection
Microsoft DependencyInjection 作为一个开源的依赖注入实现,在 .Net Core 里被广泛的使用,这里用一百行代码结合反射技术实现一个简版 IoC 容器,实现对象服务注入、构造函数参数注入等。

先来看看测试用的代码:
public class DiDemo
{
private static void Main(string[] args)
{
Services services = Services.GetInstance();
// register services
services.Add("Some string...");
services.Add(new MyService("More string..."));
services.Add<MyService>();
services.Add<MyServiceTest>();
Log(Services.Name);
var str = services.GetService<string>() as string;
Log($"Got String: {str}");
var ms = services.GetService<MyService>() as MyService;
Log($"Got MyService: {ms.DataSource}");
var mst = services.GetService<MyServiceTest>() as MyServiceTest;
mst?.Test(); // if got a service and use it
}
public static void Log(string msg)
{
string stamp = DateTime.Now.ToString("hh:mm:ss ffff");
Console.WriteLine("\n{0}:\n\t{1}", stamp, msg);
}
}
public class MyService
{
public string DataSource {get; set;}
// use DI here when constructor call
public MyService(string data = "Text for testing")
{
DataSource = data;
}
}
public class MyServiceTest
{
protected MyService _service;
// Use DI here when constructor call
public MyServiceTest(MyService service)
{
_service = service;
}
public void Test()
{
Console.WriteLine($"Testing Text from MyService: {_service.DataSource}.");
}
}
分别定义了 MyService 和 MyServiceTest 两种服务,后者用来测试前提供的字符串服务,测试构造函数的依赖注入。也可以使用其他类型,如字符串作为服务。Services 是 IoC 容器,提供几个核心方法:
- Add(object service) 注册服务实例;
- Add<T>() 通过泛型函数注册服务类型;
- GetService() 获取已经注册的服务,优先返回注册的服务实例,其次返回可以通过反射方式构造的服务实例;
IoC 容器实现语法上使用了 C# 的特色 Linq 查询语言,还有泛型:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Services
{
public static string Name = "IoC Container(IoC - Iversion of Control, DI - Dependency Injection)";
protected static Services Instance;
protected static List<object> Singletons;
protected static List<Type> ServiceTypes;
public static Services GetInstance()
{
if (Instance == null)
{
Instance = new Services();
ServiceTypes = new List<Type>{};
Singletons = new List<object>{};
}
return Instance;
}
public Services Add(Object it)
{
Singletons.Add(it);
return this;
}
public Services Add<T>()
{
ServiceTypes.Add(typeof(T));
return this;
}
public object GetService<T>()
{
var singleton = GetSingleton(typeof(T));
if (singleton != null)
{
return singleton;
}
foreach (var ti in ServiceTypes)
{
if (ti == typeof(T))
{
return Build(ti);
}
}
return new MyService("Default IoC Service");
}
protected object Build(Type ti)
{
// retrive ConstructorInfo[]
var constructors = ti.GetConstructors().OrderBy(c => c.GetParameters().Length).ToList();
if (constructors.Count == 0)
{
throw new MissingMethodException("No public constructor defined for this object");
}
foreach (var ctor in constructors)
{
// retrive ParameterInfo[]
var paras = ctor.GetParameters();
object[] parameters = new object[paras.Length];
int idx = 0;
foreach (var pa in paras)
{
var it = GetSingleton(pa.ParameterType);
if (it==null) break;
parameters[idx ++] = it;
}
if (idx < paras.Length) continue;
try
{
var instance = ctor.Invoke(parameters);
return instance;
}
catch (Exception ex)
{
Console.WriteLine("EX:"+ex.ToString());
}
}
return null;
}
protected object GetSingleton(Type ti)
{
foreach(var it in Singletons)
{
if (it.GetType() == ti)
{
return it;
}
}
return null;
}
}
进一步修改
MyServiceTest 按如下实现另外一个构造函数,加入 info 参数,在有两个构造函数的情况下,看 IoC 会执行哪个:
public class MyServiceTest
{
protected MyService _service;
protected string _info = "unchanged";
public MyServiceTest(MyService service, string info)
{
_service = service;
_info = info;
}
// Use DI here when constructor call
public MyServiceTest(MyService service)
{
_service = service;
}
public void Test()
{
Console.WriteLine($"Testing Text from MyService & info: {_service.DataSource} [{_info}].");
}
}
试着修改 IoC 实现的 Build 方法,修改 Linq 排序方式,再试试:
OrderBy(c => -c.GetParameters().Length)
网友评论