至于怎么样是依附注入容器英特网生龙活虎度有非常多的文章介绍

趁着大范围的种类尤其多,大多门类都引进了依据注入框架,个中最流行的有Castle
温泽, Autofac和Unity Container。
微软在新型版的Asp.Net
Core中自带了依赖注入的效率,有意思味能够查阅这里。
有关什么是依据注入容器网1月经有成百上千的作品介绍,这里作者将重视呈报怎样达成贰个协和的器皿,能够扶助您掌握依赖注入的原理。

容器的构想

在编辑容器在此以前,应该先想好这些容器如何行使。
容器允许注册服务和兑现项目,允许从服务类型得出服务的实例,它的行使代码应该像

var container = new Container();

container.Register<MyLogger, ILogger>();

var logger = container.Resolve<ILogger>();

最功底的容器

在上边包车型地铁构想中,Container类有四个函数,一个是Register,一个是Resolve
容器须要在Register时关联ILogger接口到MyLogger得以达成,並且须要在Resolve时领会应为ILogger生成MyLogger的实例。
以下是兑现那多个函数最幼功的代码

public class Container
{
    // service => implementation
    private IDictionary<Type, Type> TypeMapping { get; set; }

    public Container()
    {
        TypeMapping = new Dictionary<Type, Type>();
    }

    public void Register<TImplementation, TService>()
        where TImplementation : TService
    {
        TypeMapping[typeof(TService)] = typeof(TImplementation);
    }

    public TService Resolve<TService>()
    {
        var implementationType = TypeMapping[typeof(TService)];
        return (TService)Activator.CreateInstance(implementationType);
    }
}

Container在中间创设了二个服务类型(接口类型卡塔尔国到落到实处项目标目录,Resolve时行使索引找达到成项目并创建实例。
以此完成很简短,但是有成都百货上千难点,举例

  • 二个服务类型不可能对应多个完毕项目
  • 尚无对实例进行生命周期管理
  • 从没兑现构造函数注入

校订容器的构想 – 类型索引类型

要让贰个服务类型对应七个实现项目,能够把TypeMapping改为

IDictionary<Type, IList<Type>> TypeMapping { get; set; }

黄金时代旦此外提供贰个保存实例的变量,也能达成生命周期管理,但显得某个复杂了。
此间能够转变一下思路,把{服务类型=>完毕项目}改为{服务类型=>工厂函数},让生命周期的治本在工厂函数中完结。

IDictionary<Type, IList<Func<object>>> Factories { get; set; }

奇迹大家会想让客商在配备文件中切换完结项目,当时假使把键类型改成服务类型+字符串,达成起来会轻便比较多。
Resolve能够那样用:
Resolve<Service>(serviceKey: Configuration["ImplementationName"])

IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

精耕细作容器的构想 – Register和Resolve的拍卖

在规定了索引类型后,RegisterResolve的拍卖都应当跟着改正。
Register注册时应当率先依据兑现项目更换工厂函数,再把工厂函数加到服务类型对应的列表中。
Resolve肃清时应该依据服务类型找到工厂函数,然后实践工厂函数重返实例。

精雕细琢后的器皿

其生龙活虎容器新扩张了一个ResolveMany函数,用于清除多少个实例。
其余还用了Expression.Lambda编写翻译工厂函数,生成效率会比Activator.CreateInstance快数十倍。

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    public void Register<TImplementation, TService>(string serviceKey = null)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

改善后的器皿依然有以下的难题

  • 从未有过对实例进行生命周期管理
  • 并未有实现构造函数注入

金镶玉裹福禄双全实例的单例

以上面代码为例

var logger_a = container.Resolve<ILogger>();
var logger_b = container.Resolve<ILogger>();

采取方面包车型大巴器皿推行这段代码时,logger_alogger_b是多个不等的目的,如若想要每便Resolve都回来同样的指标呢?
大家得以对工厂函数实行打包,依靠闭包(Closure卡塔尔国的本事可以特别轻巧的得以完结。

private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
{
    if (!singleton)
        return originalFactory;
    object value = null;
    return () =>
    {
        if (value == null)
            value = originalFactory();
        return value;
    };
}

加上这么些函数后在Register中调用factory = WrapFactory(factory, singleton);即可。
后生可畏体化代码将要前面放出,接下去再看怎么兑现布局函数注入。

落到实处构造函数注入

以上边代码为例

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();

    var logger = container.Resolve<ILogger>();
    logger.Log("Example Message");
}

在这里段代码中,MyLogger布局时索要二个ILogWriter的实例,可是那些实例大家不可能直接传给它。
与此相类似将在求容器能够自动生成ILogWriter的实例,再传给MyLogger以生成MyLogger的实例。
要落到实处这几个成效须要使用c#中的反射机制。

把地点代码中的

var factory = Expression.Lambda<Func<object>>(Expression.New(typeof(TImplementation))).Compile();

换成

private Func<object> BuildFactory(Type type)
{
    // 获取类型的构造函数
    var constructor = type.GetConstructors().FirstOrDefault();
    // 生成构造函数中的每个参数的表达式
    var argumentExpressions = new List<Expression>();
    foreach (var parameter in constructor.GetParameters())
    {
        var parameterType = parameter.ParameterType;
        if (parameterType.IsGenericType &&
            parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            // 等于调用this.ResolveMany<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "ResolveMany",
                parameterType.GetGenericArguments(),
                Expression.Constant(null, typeof(string))));
        }
        else
        {
            // 等于调用this.Resolve<TParameter>();
            argumentExpressions.Add(Expression.Call(
                Expression.Constant(this), "Resolve",
                new [] { parameterType },
                Expression.Constant(null, typeof(string))));
        }
    }
    // 构建new表达式并编译到委托
    var newExpression = Expression.New(constructor, argumentExpressions);
    return Expression.Lambda<Func<object>>(newExpression).Compile();
}

这段代码通过反射获取了布局函数中的全数参数,并对种种参数使用ResolveResolveMany解决。
值得注意的是参数的解决是延迟的,独有在塑造MyLogger的时候才会创设MyLogWriter,那样做的收益是流入的实例不肯定需如若单例。
用表明式营造的工厂函数消释的时候的性格会超级高。

风度翩翩体化代码

容器和演示的全体代码如下

public interface ILogWriter
{
    void Write(string text);
}

public class MyLogWriter : ILogWriter
{
    public void Write(string str)
    {
        Console.WriteLine(str);
    }
}

public interface ILogger
{
    void Log(string message);
}

public class MyLogger : ILogger
{
    ILogWriter _writer;

    public MyLogger(ILogWriter writer)
    {
        _writer = writer;
    }

    public void Log(string message)
    {
        _writer.Write("[ Log ] " + message);
    }
}

static void Main(string[] args)
{
    var container = new Container();
    container.Register<MyLogWriter, ILogWriter>();
    container.Register<MyLogger, ILogger>();
    var logger = container.Resolve<ILogger>();
    logger.Log("asdasdas");
}

public class Container
{
    private IDictionary<Tuple<Type, string>, IList<Func<object>>> Factories { get; set; }

    public Container()
    {
        Factories = new Dictionary<Tuple<Type, string>, IList<Func<object>>>();
    }

    private Func<object> WrapFactory(Func<object> originalFactory, bool singleton)
    {
        if (!singleton)
            return originalFactory;
        object value = null;
        return () =>
        {
            if (value == null)
                value = originalFactory();
            return value;
        };
    }

    private Func<object> BuildFactory(Type type)
    {
        // 获取类型的构造函数
        var constructor = type.GetConstructors().FirstOrDefault();
        // 生成构造函数中的每个参数的表达式
        var argumentExpressions = new List<Expression>();
        foreach (var parameter in constructor.GetParameters())
        {
            var parameterType = parameter.ParameterType;
            if (parameterType.IsGenericType &&
                parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                // 等于调用this.ResolveMany<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "ResolveMany",
                    parameterType.GetGenericArguments(),
                    Expression.Constant(null, typeof(string))));
            }
            else
            {
                // 等于调用this.Resolve<TParameter>();
                argumentExpressions.Add(Expression.Call(
                    Expression.Constant(this), "Resolve",
                    new [] { parameterType },
                    Expression.Constant(null, typeof(string))));
            }
        }
        // 构建new表达式并编译到委托
        var newExpression = Expression.New(constructor, argumentExpressions);
        return Expression.Lambda<Func<object>>(newExpression).Compile();
    }

    public void Register<TImplementation, TService>(string serviceKey = null, bool singleton = false)
        where TImplementation : TService
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            factories = new List<Func<object>>();
            Factories[key] = factories;
        }
        var factory = BuildFactory(typeof(TImplementation));
        WrapFactory(factory, singleton);
        factories.Add(factory);
    }

    public TService Resolve<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        var factory = Factories[key].Single();
        return (TService)factory();
    }

    public IEnumerable<TService> ResolveMany<TService>(string serviceKey = null)
    {
        var key = Tuple.Create(typeof(TService), serviceKey);
        IList<Func<object>> factories;
        if (!Factories.TryGetValue(key, out factories))
        {
            yield break;
        }
        foreach (var factory in factories)
        {
            yield return (TService)factory();
        }
    }
}

写在终极

以此容器完结了三个依据注入容器应该有的根本功能,可是照旧有过多不足的地点,举例

  • 不援助线程安全
  • 不扶植非泛型的注册和解决
  • 不扶持只用于钦定范围内的单例
  • 不帮助成员注入
  • 不援救动态代理落成AOP

我在ZKWeb网页框架中也应用了团结编写的器皿,唯有300多行不过能够知足实际项目的选用。
少年老成体化的源代码能够翻看这里和这里。

微软从.Net
Core起头提供了DependencyInjection的肤浅接口,那为依靠注入提供了叁个标准。
在未来或者不会再必要学习Castle 温泽,
Autofac等,而是从来动用微软提供的标准接口。
即使实际的兑现形式离我们原本越远,可是明白一下它们的准绳总是有实益的。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website