.NET高级应用---自定义Ioc容器(附带源码)

目录

[1. IoC容器对象项目结构的影响](#1. IoC容器对象项目结构的影响)

代码体现:

[2. IoC容器中的对象依赖注入](#2. IoC容器中的对象依赖注入)

代码体现:

[3. IoC概念理解与自定义IoC容器](#3. IoC概念理解与自定义IoC容器)

代码体现:

[4. 自定义IoC容器的基本控制逻辑](#4. 自定义IoC容器的基本控制逻辑)

[5. IOC瞬态模式与单例模式逻辑整合](#5. IOC瞬态模式与单例模式逻辑整合)

代码体现:

[6. 无限层级的依赖注入逻辑处理](#6. 无限层级的依赖注入逻辑处理)

代码体现:

[7. 属性注入逻辑处理](#7. 属性注入逻辑处理)

代码体现:

[8. 单接口多实现注入逻辑处理](#8. 单接口多实现注入逻辑处理)

代码体现:

9.完整代码

总结


1. IoC容器对象项目结构的影响

IoC容器的作用是将对象的创建和依赖关系的管理从代码中解耦出来,交给容器来处理。通过这种方式,项目的结构会更加清晰和模块化。

代码体现:

在你的代码中,MyIoc 类是一个自定义的IoC容器。它通过 _instenceDic 字典来管理对象的注册和实例化。这种设计使得对象的创建和依赖关系不再硬编码在业务逻辑中,而是由容器统一管理。

  • 解耦 :对象的创建和依赖关系由 MyIoc 管理,业务代码只需要通过 Resolve 方法获取实例,无需关心对象的创建过程。
  • 模块化:通过接口和抽象类定义依赖关系,具体的实现类由容器注入,增强了代码的模块化。

2. IoC容器中的对象依赖注入

依赖注入(DI)是IoC的一种实现方式。通过依赖注入,对象的依赖关系由外部容器在运行时注入,而不是在对象内部硬编码。

代码体现:

MyIoc 类中,依赖注入主要通过以下方式实现:

  • 构造函数注入 :在 CreateInstence 方法中,通过反射获取构造函数的参数,并从容器中解析这些参数,最终创建对象实例。
  • 属性注入 :在 CreateInstence 方法中,通过反射获取对象的属性,并根据属性的标记(如 DependyAttribute)从容器中解析并注入依赖。

例如:

  • 构造函数注入
cs 复制代码
// 构造函数注入
ConstructorInfo[] cis = _instenceDic[key].ObjectType.GetConstructors();
ParameterInfo[] cpis = cis[0].GetParameters();
List<object> objects = new List<object>();
foreach (ParameterInfo cpi in cpis)
{
    string paramTypeKey = cpi.ParameterType.FullName;
    if (_instenceDic.ContainsKey(paramTypeKey))
    {
        objects.Add(_instenceDic[paramTypeKey].Instence);
    }
}
var obj = Activator.CreateInstance(_instenceDic[key].ObjectType, objects.ToArray());
  • 属性注入
cs 复制代码
PropertyInfo[] pis = type.GetProperties();
foreach (var pi in pis)
{
    if (pi.IsDefined(typeof(DependyAttribute), false))
    {
        var attr = pi.GetCustomAttribute<DependyAttribute>();
        string paramTypeKey = pi.PropertyType.FullName + (string.IsNullOrEmpty(attr.Token) ? "" : "_#%%$#_" + attr.Token);
        if (_instenceDic.ContainsKey(paramTypeKey))
        {
            pi.SetValue(obj, _instenceDic[paramTypeKey].Instence);
        }
    }
}

3. IoC概念理解与自定义IoC容器

IoC的核心思想是将对象的创建和依赖关系的管理交给外部容器,而不是在代码中硬编码。通过这种方式,代码的灵活性和可维护性得到了提升。

代码体现:

MyIoc 是一个自定义的IoC容器,它通过字典 _instenceDic 来存储注册的类型和实例。每个注册的类型都包含一个 InstenceModel 对象,该对象记录了类型的元数据(如是否为单例)以及实例本身。

  • 注册类型 :通过 RegisterRegisterSingle 方法将类型注册到容器中。
  • 解析实例 :通过 Resolve 方法从容器中获取实例。

4. 自定义IoC容器的基本控制逻辑

MyIoc 类的基本控制逻辑如下:

  • 注册类型
    • Register<T>():注册瞬时对象(每次请求都创建新实例)。
    • RegisterSingle<T>():注册单例对象(整个应用程序生命周期内只创建一个实例)。
    • Register<TFrom, TTo>():注册接口和实现类的关系。
  • 解析实例
    • Resolve<T>():从容器中获取实例。如果注册的类型是单例模式,则返回同一个实例;如果是瞬时模式,则每次返回新的实例。
  • 依赖注入
    • 在创建实例时,CreateInstence 方法会递归地解析构造函数参数和属性依赖,确保所有依赖都被正确注入。

5. IOC瞬态模式与单例模式逻辑整合

MyIoc 中,瞬态模式和单例模式的逻辑通过 InstenceModel 中的 IsSinglton 属性进行区分:

  • 瞬态模式 :每次调用 Resolve 时,都会创建一个新的实例。
  • 单例模式 :只有在第一次调用 Resolve 时创建实例,后续调用都返回同一个实例。

代码体现:

cs 复制代码
if (_instenceDic[key].IsSinglton)
{
    if (_instenceDic[key].Instence is null)
    {
        lock (_instanceLock)
        {
            if (_instenceDic[key].Instence is null)
            {
                return (T)CreateInstence(key, typeof(T));
            }
            return (T)_instenceDic[key].Instence;
        }
    }
    return (T)_instenceDic[key].Instence;
}
else
{
    // 在瞬时模式下,每次都调用
    return (T)CreateInstence(key, typeof(T));
}

6. 无限层级的依赖注入逻辑处理

CreateInstence 方法中,通过递归调用 CreateInstence 来处理无限层级的依赖注入。当某个对象的构造函数参数或属性依赖其他对象时,容器会递归地解析这些依赖,直到所有依赖都被满足。

代码体现:

cs 复制代码
if (_instenceDic.ContainsKey(paramTypeKey))
{
    if (_instenceDic[paramTypeKey].IsSinglton)
    {
        if (_instenceDic[paramTypeKey].Instence is null)
        {
            _instenceDic[paramTypeKey].Instence = CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
        }
    }
    else
    {
        _instenceDic[paramTypeKey].Instence = CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
    }
    objects.Add(_instenceDic[paramTypeKey].Instence);
}

7. 属性注入逻辑处理

CreateInstence 方法中,通过反射获取对象的属性,并根据属性的标记(如 DependyAttribute)从容器中解析并注入依赖。

代码体现:

cs 复制代码
PropertyInfo[] pis = type.GetProperties();
foreach (var pi in pis)
{
    if (pi.IsDefined(typeof(DependyAttribute), false))
    {
        var attr = pi.GetCustomAttribute<DependyAttribute>();
        string paramTypeKey = pi.PropertyType.FullName + (string.IsNullOrEmpty(attr.Token) ? "" : "_#%%$#_" + attr.Token);
        if (_instenceDic.ContainsKey(paramTypeKey))
        {
            pi.SetValue(obj, _instenceDic[paramTypeKey].Instence);
        }
    }
}

8. 单接口多实现注入逻辑处理

MyIoc 中,通过 Register<TFrom, TTo>RegisterSingle<TFrom, TTo> 方法支持单接口多实现的注入。通过 token 参数,可以为同一个接口的不同实现指定不同的标识符,从而在解析时区分不同的实现。

代码体现:

cs 复制代码
public void Register<TFrom, TTo>(string token = "") where TTo : TFrom
{
    string key = typeof(TFrom).FullName + (String.IsNullOrEmpty(token) ? "" : "_#%%$#_" + token);
    _instenceDic.Add(key, new InstenceModel { ObjectType = typeof(TTo), IsSinglton = false });
}

在解析时,可以通过指定 token 来获取不同的实现:

cs 复制代码
var serviceA = ioc.Resolve<IService>("A");
var serviceB = ioc.Resolve<IService>("B");

9.完整代码

cs 复制代码
using System;
using System.Collections.Generic;
using System.Reflection;

namespace MvvmLightLesson.Base
{
    public class MyIoc
    {
        private static readonly object _instanceLock = new object();

        private static MyIoc instance;

        private Dictionary<string, InstenceModel> _instenceDic = new Dictionary<string, InstenceModel>();

        private MyIoc()
        { }

        public static MyIoc Default
        {
            get
            {
                if (instance == null)
                {
                    lock (_instanceLock)
                    {
                        if (instance == null)
                        {
                            instance = new MyIoc();
                        }
                    }
                }

                return instance;
            }
        }

        // 瞬时状态
        public void Register<T>()
        {
            _instenceDic.Add(
                typeof(T).FullName!,
                new InstenceModel { ObjectType = typeof(T), IsSinglton = false });
        }

        public void Register<TFrom, TTo>(string token = "") where TTo : TFrom
        {
            string key =
                typeof(TFrom).FullName +
                (String.IsNullOrEmpty(token) ? "" : "_#%%$#_" + token);

            _instenceDic.Add(
                key,
                new InstenceModel { ObjectType = typeof(TTo), IsSinglton = false });
        }

        // 单例状态
        public void RegisterSingle<T>()
        {
            _instenceDic.Add(
                typeof(T).FullName!,
                new InstenceModel { ObjectType = typeof(T), IsSinglton = true });
        }

        public void RegisterSingle<TFrom, TTo>(string token = "") where TTo : TFrom
        {
            string key =
                typeof(TFrom).FullName +
                (String.IsNullOrEmpty(token) ? "" : "_#%%$#_" + token);

            _instenceDic.Add(
                key,
                new InstenceModel { ObjectType = typeof(TTo), IsSinglton = true });
        }

        public T Resolve<T>()
        {
            string key = typeof(T).FullName;
            if (_instenceDic.ContainsKey(key))
            {
                if (_instenceDic[key].IsSinglton)
                {
                    if (_instenceDic[key].Instence is null)
                    {
                        lock (_instanceLock)
                        {
                            if (_instenceDic[key].Instence is null)
                            {
                                // 在单例模式下,之前没有创建实例的前提下调用
                                return (T)CreateInstence(key, typeof(T));
                            }
                            return (T)_instenceDic[key].Instence;
                        }
                    }
                    return (T)_instenceDic[key].Instence;
                }
                else
                {
                    // 在瞬时模式下,每次都调用
                    return (T)CreateInstence(key, typeof(T));
                }
            }
            return default(T);
        }

        private object CreateInstence(string key, Type type)
        {
            #region 检查构造函数的参数

            // 如果有参数,根据类型动态创建实例
            ConstructorInfo[] cis = _instenceDic[key].ObjectType.GetConstructors();
            ParameterInfo[] cpis = cis[0].GetParameters();
            // 参数列表
            List<object> objects = new List<object>();
            foreach (ParameterInfo cpi in cpis)
            {
                // AAA   MySqlDA.
                string paramTypeKey = cpi.ParameterType.FullName;
                if (cpi.IsDefined(typeof(DependyAttribute), false))
                {
                    var attr = cpi.GetCustomAttribute<DependyAttribute>();
                    paramTypeKey += string.IsNullOrEmpty(attr.Token) ? "" : "_#%%$#_" + attr.Token;
                }
                if (_instenceDic.ContainsKey(paramTypeKey))
                {
                    if (_instenceDic[paramTypeKey].IsSinglton)
                    {
                        if (_instenceDic[paramTypeKey].Instence is null)
                        {
                            //_instenceDic[paramTypeKey].Instence =
                            //    Activator.CreateInstance(_instenceDic[paramTypeKey].ObjectType);
                            _instenceDic[paramTypeKey].Instence =
                                CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
                        }
                    }
                    else
                    {
                        _instenceDic[paramTypeKey].Instence =
                                CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
                    }

                    objects.Add(_instenceDic[paramTypeKey].Instence);
                }
                else
                    objects.Add(null);
            }

            #endregion 检查构造函数的参数

            // 创建对象实例,创建之前需要准备好这个对象的构造函数的参数
            var obj = Activator.CreateInstance(_instenceDic[key].ObjectType, objects.ToArray());
            _instenceDic[key].Instence = obj;

            #region 检查属性的注入

            //
            // 哪些属性需要注入   可以通过特性进行区分
            PropertyInfo[] pis = type.GetProperties();
            //type.GetFields();// 获取所有字段
            foreach (var pi in pis)
            {
                // 判断属性上是否有特定标记,如果有,实例化这个属性
                if (pi.IsDefined(typeof(DependyAttribute), false))
                {
                    var attr = pi.GetCustomAttribute<DependyAttribute>();

                    // 属性注入的时候
                    string paramTypeKey = pi.PropertyType.FullName +
                        (string.IsNullOrEmpty(attr.Token) ? "" : "_#%%$#_" + attr.Token);

                    if (_instenceDic.ContainsKey(paramTypeKey))
                    {
                        if (_instenceDic[paramTypeKey].IsSinglton)
                        {
                            if (_instenceDic[paramTypeKey].Instence is null)
                            {
                                _instenceDic[paramTypeKey].Instence =
                                    CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
                            }
                        }
                        else
                        {
                            _instenceDic[paramTypeKey].Instence =
                                    CreateInstence(paramTypeKey, _instenceDic[paramTypeKey].ObjectType);
                        }

                        // 如果已经创建了实例,设置给当前对象的对应属性
                        // 第一个参数,指这个值要设置给哪个对象实例的这个属性
                        pi.SetValue(obj, _instenceDic[paramTypeKey].Instence);
                    }
                }
            }

            #endregion 检查属性的注入

            return obj;
        }

        // 1、对象中的参数未注入
        // 2、多个实现的使用
        // 3、属性注入
        // 4、瞬时注入/单例模式
    }

    internal class InstenceModel
    {
        public object Instence { get; set; }
        public bool IsSinglton { get; set; }
        public Type ObjectType { get; set; }
    }
}

总结

通过 MyIoc 这个自定义IoC容器,我们可以看到IoC容器的核心功能:

  • 对象生命周期管理:支持瞬态模式和单例模式。
  • 依赖注入:支持构造函数注入和属性注入。
  • 灵活性:支持单接口多实现的注入。

虽然 MyIoc 是一个简单的实现,但它清晰地展示了IoC容器的核心思想和实现方式。在实际项目中,可以使用更成熟的IoC容器(如Autofac、Unity等)来获得更强大的功能和更好的性能。

相关推荐
夏天的味道٥1 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
IT、木易2 小时前
大白话JavaScript实现一个函数,将字符串中的每个单词首字母大写。
开发语言·前端·javascript·ecmascript
Mr.NickJJ3 小时前
JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件
开发语言·javascript·react.js
Archer1944 小时前
C语言——链表
c语言·开发语言·链表
My Li.4 小时前
c++的介绍
开发语言·c++
功德+n4 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven
达斯维达的大眼睛4 小时前
qt小项目,简单的音乐播放器
开发语言·qt
面会菜.4 小时前
C语言(队列)
c语言·开发语言
香精煎鱼香翅捞饭5 小时前
java通用自研接口限流组件
java·开发语言
-凌凌漆-5 小时前
【C#】async与await介绍
开发语言·c#