美文网首页
100行代码带你进入 IoC 和 DI - Dependency

100行代码带你进入 IoC 和 DI - Dependency

作者: 坚果jimbowhy | 来源:发表于2020-06-01 07:16 被阅读0次

Reflection & DI - Dependency Injection

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


DI - Dependency Injection

先来看看测试用的代码:

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)

参考

相关文章

网友评论

      本文标题:100行代码带你进入 IoC 和 DI - Dependency

      本文链接:https://www.haomeiwen.com/subject/omzdzhtx.html