65.9K
CodeProject 正在变化。 阅读更多。
Home

BarbarianIOC:一个简单的 IOC 容器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (39投票s)

2013年2月25日

CPOL

15分钟阅读

viewsIcon

79910

downloadIcon

533

一个简单的 IOC 容器,纯粹为了好玩。

 

文章演示代码: BarbarianIOC.zip

 

引言

在工作中,我使用过多种IOC容器,包括:

我还用过Unity/StructureMap/AutoFac等。它们都非常优秀且功能丰富。

对于那些不知道IOC是什么意思的朋友,它代表“控制反转”(Inversion  Of Control)。其描述如下:

在软件工程中,控制反转(IoC)是一种编程技术,在这里以面向对象编程为表达形式,通过一个组装对象在运行时绑定对象耦合,并且通常在编译时无法通过静态分析得知。

在传统的编程中,业务逻辑的流程由静态分配的对象决定。通过控制反转,流程取决于组装对象实例化的对象图,并通过抽象定义对象交互来实现。绑定过程通过依赖注入实现,尽管有些人认为使用服务定位器也能提供控制反转。

为了让组装对象能够相互绑定,对象必须具备兼容的抽象。例如,类A可能委托行为给接口I,而类B实现了该接口I;组装对象实例化A和B,然后将B注入A。

实际上,控制反转是一种软件构建风格,其中可重用代码控制特定问题的代码的执行。它带有可重用代码和问题特定代码独立开发的强烈含义,这通常会形成一个单一的集成应用程序。控制反转作为设计指导原则,有以下目的:

  • 将特定任务的执行与实现解耦。
  • 每个模块都可以专注于其设计目标。
  • 模块不对其他系统做什么做假设,而是依赖于它们的契约。
  • 替换模块对其他模块没有副作用。
  • 控制反转有时被戏称为“好莱坞原则:别给我们打电话,我们会给您打电话”,因为程序逻辑是针对抽象(如回调)运行的。

维基百科:更新日期 2013/02/25

事情是这样的,我一直想自己尝试做一个这样的东西,看看里面包含什么。我不想做得太复杂,只想实现以下功能:

  1. 实例配置:单例/瞬态
  2. 简单的注册过程,也许是一种流畅的接口
  3. 使用表达式API编译为委托,以快速创建对象
  4. 构造函数/属性注入
  5. 提供接受非IOC持有的构造函数参数的能力

所以,以上几点就是**所有**我想要实现的功能。正如我所说,市面上已经有很多功能齐全的IOC容器(上面我已列举几个),本文中的容器更像是一个学习练习,我将其分享出来,以防其他人对这类东西感兴趣。

我将我的容器命名为BarbarianIOC,因为现有的容器似乎都有这些简短响亮的名字,这有点像我名字的谐音,而且如果我刮完胡子,我确实有点像个野蛮人。

好了,这就是本文的重点,但在我们深入研究之前,请务必阅读下面的重要说明。

重要提示

我应该指出,您应该坚持使用市面上主要的IOC容器之一,因为这只是一个为了了解如何创建自己的IOC容器的练习。这并不是说我不满意它,我非常满意,并且我认为通过更多的调整,我可以使其接近市面上“正式”的IOC容器,但我知道,这种调整永远不会发生,因为我总是 eager to move on to something new(渴望去尝试新事物)。所以,是的,坚持使用市面上那些大型的“正式”IOC容器。

 

它有什么功能

在本节中,我将向您展示如何使用BarbarianIOC,以及它在典型应用程序中的样子。

如何配置容器

容器可以使用类似以下这种流畅的界面进行配置:

Container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)

//Register ALL components
container.RegisterComponents(
        //where you can use concrete type
        new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
        //or you can use an interface and it's implementation
        new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
        //and you can also declare singleton instance mode if you like
        new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
        //and even supply some non IOC provided constructor params by way of an anonymous object
        new Component().For<SomeFooDependantClass>()
            .DependsOn(new 
                { 
                    age=someMockAge
                })
            .WithInstanceMode(InstanceMode.Transient)
    );
            
//allow the container to wire stuff up (essentially create Expression.New for all 
//components to allow Container to compile and create some quicker lookup delegates)
container.WireUp();

 

允许非IOC构造函数参数

我想要解决的一个重要问题是,不仅要允许提供容器生成的构造函数参数,还要允许提供用户指定的构造函数参数。我决定使用一个简单的匿名对象来做到这一点,您只需指定构造函数参数的名称及其值。下面展示了一个示例,我们可以在配置IOC容器时提供这些额外的非IOC构造函数参数,如下所示:

container.RegisterComponents(
        new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
        new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
        new Component().For<SomeFooDependantClass>()
            .DependsOn(new 
                { 
                    age=someMockAge
                })
            .WithInstanceMode(InstanceMode.Transient)
    );

请注意DependsOn(..)方法的用法以及提供age非IOC创建的构造函数参数的匿名对象,这个参数可能被某些类如以下所示的那样需要:

public class SomeFooDependantClass
{
    /// <summary>
    /// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
    /// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
    /// Type from the BarbarianIOC.Container
    /// </summary>
    [DependencyConstructorAttribute]
    public SomeFooDependantClass(Foo foo, int age, IBaz baz)
    {


    }
}

这表明我们有一个类,它期望一个age构造函数参数值,以及一些应该来自IOC容器的其他构造函数参数。请注意,非IOC生成的构造函数参数的顺序位于构造函数参数列表的中间,它也可以在任何位置,完全支持IOC/非IOC创建的构造函数参数的混合搭配。我对这个特性很满意。

 

构造函数注入

本文附带的IOC容器支持构造函数注入,因此能够满足以下构造函数:

public class SomeIBazDependantClass
{
    private IBaz somePropBaz;

    [DependencyConstructorAttribute]
    public SomeIBazDependantClass(IBaz baz)
    {

    }
}

如前所述,还可以包含额外的非IOC构造函数参数,例如我们刚才看到的age参数。以下是注释您的类以告知它将尝试满足哪个构造函数依赖项的示例:

public class SomeFooDependantClass
{
    /// <summary>
    /// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
    /// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
    /// Type from the BarbarianIOC.Container
    /// </summary>
    [DependencyConstructorAttribute]
    public SomeFooDependantClass(Foo foo, int age, IBaz baz)
    {


    }
}

从上面的代码可以看出,我选择使用了一个名为DependencyConstructorAttribute的专用属性,您**必须**使用它来标记您选择的构造函数。这将指示本文提供的IOC容器,应该使用哪个构造函数从容器中创建对象。

现在,有些人可能会认为这违反了**关注点分离(S**eparation  **Of  **Concerns, SOC)的原则,老实说,它确实有点,因为我们的对象现在通过这些特殊的DependencyConstructorAttributes包含了IOC特定的数据。然而,如果没有这个 DependencyConstructorAttribute,选择哪个ConstructorInfo将会是一个非常容易出错的经历。IOC容器是应该选择参数最多的?参数最少的?还是参数最多的可解析IOC参数的?这不是一个容易的决定。我可以编写一些花哨的流畅API注册代码来实现它,但最终,我认为使用一个专门的属性(DependencyConstructorAttribute)是合理的,用户可以使用它来修饰他们的对象,告诉IOC容器,嘿,你需要使用这个构造函数来创建对象。

IOC/非IOC构造函数参数的顺序**不**重要,您可以随意混合搭配IOC/非IOC提供的构造函数参数。稍后我们会讲到这是如何工作的。

 

属性注入

本文附带的IOC容器还支持属性注入,配置如下:

public class SomeIBazDependantClass
{
    private IBaz somePropBaz;

    /// <summary>
    /// BarbarianIOC also allows for property injection (provided you use the 
    /// <see cref="BarbarianIOC.Attributes.DependencyAttribute">DependencyAttribute</see>)
    /// </summary>
    [Dependency]
    public IBaz SomePropBaz
    {
        get 
        { 
            return somePropBaz; 
        }
        set 
        { 
            somePropBaz = value; 
        }
    }
}

与构造函数注入一样,我选择使用一个名为DependencyAttribute的专用属性,它指示IOC容器应该尝试注入哪些属性。

 

解析实例

当您需要从容器中获取对象实例时,您只需要Resolve它,操作如下:

SomeIBazDependantClass x1 = container.Resolve<SomeIBazDependantClass>();

从那里,您应该能够使用该对象实例,它将自动解析其所有的构造函数/属性IOC/非IOC依赖项(前提是您的组件注册配置正确)。

以下是本文附带的代码中从容器解析出的一个对象的示例,您可以清楚地看到它已经提供了所有属性/构造函数值。

 

它是如何工作的

好了,现在我向您展示了如何使用它,但我敢打赌您想知道它是如何工作的。幸运的是,在本节中,我将为您讲解代码,您可以了解其细节的内部工作原理。希望这会很有趣。

注册实例

注册组件(无论是服务实例还是具体类型)都使用一个流畅的接口,以Component构建器开始,它用于创建一个ComponentRegistration实例。以下方法可用于构建 ComponentRegistration条目:

  • Component For<TCOMP>():用于注册具体类型,并负责创建实例。
  • Component ServiceFor<IInt, TCOMP>():用于注册实现特定接口的服务。
  • Component DependsOn<T>(T dependencies):用于提供非IOC容器满足的构造函数参数。
  • ComponentRegistration WithInstanceMode(InstanceMode instanceMode):这**必须**是 Component构建器流畅API流程中最后调用的方法,它负责创建最终的 ComponentRegistration实例。

 

因此,考虑到这一点,让我们看看IOC容器如何使用这些 ComponentRegistration对象。这都归功于一个名为RegisterComponents的简单方法,该方法如下所示,它只是将注册添加到Container持有的集合中:

public void RegisterComponents(params ComponentRegistration[] registrations)
{
    lock (syncLock)
    {
        foreach (ComponentRegistration componentRegistration in registrations.ToList())
        {
            components.Add(componentRegistration, null);
        }
    }
}

所以,现在我们知道我们致力于创建传递给ContainerComponentRegistration对象,但这些ComponentRegistration对象是什么样的呢?它们看起来是这样的:

public class ComponentRegistration
{
    public Type TypeToLookFor { get; private set; }
    public Type TypeToCreate { get; private set; }
    public InstanceMode InstanceMode { get; set; }
    public bool HasManualConstructorParameters { get; set; }
    public List<ConstructorParameterDependency> DependsOnValues { get; set; }

    public ComponentRegistration(Type typeToCreate) : this(typeToCreate, typeToCreate)
    {


    }

    public ComponentRegistration(Type typeToLookFor, Type typeToCreate)
    {
        TypeToLookFor = typeToLookFor;
        TypeToCreate = typeToCreate;
        DependsOnValues = new List<ConstructorParameterDependency>();
    }
}

现在,让我们继续看特定的Component构建器方法。

 

注册具体实例

这是您通常使用Component构建器方法注册具体类型的方式:

new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient)

现在让我们看看它是如何工作的,这很简单,它负责创建一个 ComponentRegistration实例,该实例将在 Component流畅API结束时返回。因此,当您看到“return this”在接下来的几个方法中,那就是流畅API在起作用。

public Component For<TCOMP>()
{
    componentRegistration = new ComponentRegistration(typeof(TCOMP));
    return this;
}

注册服务实例

这是您通常使用Component构建器方法注册接口和实现类型的方式:

 new Component().ServiceFor<IBaz,Baz>().WithInstanceMode(InstanceMode.Transient)

现在让我们看看它是如何工作的,这很简单,它负责创建一个 ComponentRegistration实例,该实例将在 Component流畅API结束时返回。

public Component ServiceFor<TInt, TCOMP>()
{
    componentRegistration = new ComponentRegistration(typeof(TInt), typeof(TCOMP));
    return this;
}

 

指定实例模式

ComponentRegistration WithInstanceMode(InstanceMode instanceMode)是所有Component构建器方法中最简单的,它只是在正在构建的ComponentRegistration上设置InstanceMode,然后返回正在构建的当前ComponentRegistration

这是ComponentRegistration  WithInstanceMode(InstanceMode instanceMode) Component构建器方法的**全部**代码:

public ComponentRegistration WithInstanceMode(InstanceMode instanceMode)
{
    if (componentRegistration == null)
    {
        throw new ContainerConfigurationException("Configuration error WithInstanceMode<> MUST be last");
    }
    else
    {
        componentRegistration.InstanceMode = instanceMode;
        return componentRegistration;
    }
}

 

提供额外的非IOC构造函数参数

我真正想要实现的一个目标是能够满足那些本不应来自IOC容器的构造函数参数。本质上,我想要的是能够将IOC容器满足的构造函数参数与非IOC容器满足的构造函数参数混合搭配。其中非IOC容器满足的构造函数参数可能是:

  • 应用程序设置
  • 连接字符串
  • 静态值

如前所述,我们使用Component流畅接口来构建一个ComponentRegistration对象。用于添加非IOC容器满足的构造函数参数的相关Component构建器方法是下面显示的DependsOn( )方法。

public Component DependsOn<T>(T dependencies)
{
    if (componentRegistration == null)
    {
        throw new ContainerConfigurationException(
            "Configuration error DependsOn<> MUST be called after For<> or ImplementedBy<>");
    }
    else
    {
        List<ConstructorParameterDependency> constructorDependencies = 
		new List<ConstructorParameterDependency>();
        foreach (string name in typeof(T).GetConstructors()[0].GetParameters()
                                        .Select(p => p.Name))
        {
            PropertyInfo property = typeof(T).GetProperty(name);
            ParameterExpression param = Expression.Parameter(typeof(T), "x");
            Expression propertyAccess = Expression.Property(param, property);
            Expression convert = Expression.Convert(propertyAccess, typeof(object));
            Func<T, object> lambda = 
		Expression.Lambda<Func<T, object>>(convert, param).Compile();
            var result = lambda(dependencies);
            constructorDependencies.Add(new ConstructorParameterDependency(
                property.PropertyType, name, Expression.Constant(result)));
        }

        if (constructorDependencies.Any())
        {
            componentRegistration.HasManualConstructorParameters = true;
            componentRegistration.DependsOnValues = constructorDependencies;
        }
        else
        {
            componentRegistration.HasManualConstructorParameters = false;
        }
        return this;
    }
}

这里的基本思想是,我们检查传递到DependsOn( )方法中的匿名对象,并为匿名对象上找到的每个属性/属性值构建一个List<ConstructorParameterDependency>。这些将在稍后由IOC容器检查,以确定哪些构造函数参数值应该来自哪里。本质上,IOC容器根据其Position属性对 ConstructorParameterDependency进行排序。

我们也可以在这里使用一些反射,因为一旦IOC容器请求某个类型的第一个实例,这些构造函数参数值就会被缓存起来,所以反射可能是一个比使用Expression树更好的选择,但在这种情况下,性能差异很小,所以我坚持使用Expression API。

我们已经看到了它的用法示例,但为了完整起见,这里再次展示:

//Register ALL components
container.RegisterComponents(
    //and even supply some non IOC provided constructor params by way of an anonymous object
    new Component().For<SomeFooDependantClass>()
        .DependsOn(new 
            { 
                age = someMockAge
            })
        .WithInstanceMode(InstanceMode.Transient)
);

 

实例模式的工作原理

目前,本文附带的IOC容器仅支持2种实例模式:

  1. 单例:使用此实例模式解析的每个对象实例都应该是完全相同的实例。
  2. 瞬态:使用此实例模式解析的每个对象实例都应该是全新的唯一实例。

瞬态实例模式

瞬态模式使用一个简单的工厂,该工厂使用IOC容器缓存的delegate。当调用delegateExpression.New被编译成delegate)来返回一个新的对象实例时,TransientFactory如下所示:

/// <summary>
/// Transient object factory which returns a new instance 
/// when Create() method is called
/// </summary>
public class TransientFactory : IFactoryProvider
{
    private Delegate objectCreator;


    public TransientFactory(Delegate objectCreator)
    {
        this.objectCreator = objectCreator;
    }


    public object Create()
    {
        return objectCreator.DynamicInvoke();
    }
}

单例实例模式

单例模式也使用一个简单的工厂,它本质上使用IOC容器缓存的delegate。当delegateExpression.New被编译成delegate)用作Lazy<T>的值时(这是一种创建线程安全单例的巧妙方法),通过调用用作Lazy<T>.Value的委托返回的新对象实例。SingletonFactory如下所示:

/// <summary>
/// Singleton object factory which returns the same singleton instance 
/// when Create() method is called
/// </summary>
public class SingletonFactory : IFactoryProvider
{
    private Lazy<object> singletonCreator;

    public SingletonFactory(Delegate objectCreator)
    {
        singletonCreator = new Lazy<object>(() => objectCreator.DynamicInvoke());
    }

    public object Create()
    {
        return singletonCreator.Value;
    }
}

付诸实践

基于我们上面讨论的内容,假设我们有以下IOC容器配置:

Container container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)

//Register ALL components
container.RegisterComponents(
    //where you can use concrete type
    new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
    //or you can use an interface and it's implementation
    new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
    //and you can also declare singleton instance mode if you like
    new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
    //and even supply some non IOC provided constructor params by way of an anonymous object
    new Component().For<SomeFooDependantClass>()
        .DependsOn(new 
            { 
                age=someMockAge
            })
        .WithInstanceMode(InstanceMode.Transient)
);

当我们在下面的简单测试中检查时,我们得到了我们想要/期望的结果,基于上面的配置(点击下方查看大图):

 

将所有内容联系起来

注册完所有Component后,您需要告诉容器将所有内容连接起来。简而言之,它的作用是为每个构造函数参数创建一个Expression.Constant委托,确保遵守所请求的InstanceMode,并创建一个最终的委托,该委托负责创建注册类型,该委托将被Resolve<T>方法使用。

它递归地完成此操作,直到所有注册组件的构造函数依赖项都有匹配的委托来创建它们的构造函数参数,并且有一个最终的委托负责创建注册类型,同时还要考虑所请求的InstanceMode

这只对每个类型执行一次。我们最终得到的是一系列用于创建构造函数参数的委托(本质上是工厂),我们使用它们来为每个IOC所需的构造函数参数创建List<ConstructorParameterDependency>(与非IOC生成的构造函数参数相同)。然后将其与使用DependsOn(..)方法创建的任何 ConstructorParameterDependency实例合并。

最后一步是根据其Position属性对List<ConstructorParameterDependency>进行排序,并在Expression.Lambda中使用Expression.New来创建一个用于创建正在检查的实例的工厂。这也将考虑InstanceMode

Container的最相关方法如下所示:

public void WireUp()
{
    foreach (ComponentRegistration key in components.Where(c => c.Value == null).Select(c => c.Key).ToList())
    {
        CreateFactory(key, GetConstructorDelegateForType(key.TypeToCreate), key.InstanceMode);
    }
}

private Delegate GetConstructorDelegateForType(Type type)
{
    ComponentRegistration componentRegistration = null;

    //look for constructor that is marked with DependencyConstructorAttribute, 
    //if there is none just try and go for the default constructor
    ConstructorInfo ctor = type.GetConstructors()
        .Where(x => x.GetCustomAttributes(typeof(DependencyConstructorAttribute), false).Count() > 0).SingleOrDefault();

    if (ctor == null)
    {
        ctor = type.GetConstructors()[0];
    }

    foreach (var ctorArg in ctor.GetParameters())
    {
        bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
        if (!isParamCoveredByManualRegistration)
        {
            bool parameterKeyFound = components.Keys.Any(x => x.TypeToCreate == ctorArg.ParameterType || 
                ctorArg.ParameterType.IsAssignableFrom(x.TypeToLookFor));
                    
            if (!parameterKeyFound)
            {
                throw new ContainerConfigurationException(string.Format("Couldn't find ctor argument {0}", ctorArg.GetType()));
            }
            else
            {
                componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
                if (components[componentRegistration] == null)
                {
                    Delegate delegateForType = GetConstructorDelegateForType(componentRegistration.TypeToCreate);
                    CreateFactory(componentRegistration, delegateForType, componentRegistration.InstanceMode);
                }
            }
        }
    }

    List<ConstructorParameterDependency> args = new List<ConstructorParameterDependency>();
    foreach (var ctorArg in ctor.GetParameters())
    {
        bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
        if (!isParamCoveredByManualRegistration)
        {
            componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
            args.Add(new ConstructorParameterDependency(
                ctorArg.ParameterType,
                ctorArg.Name,
                Expression.Constant(components[componentRegistration].Create()),
                ctorArg.Position));
        }
    }

    componentRegistration = FetchComponentRegistration(type);
    if (componentRegistration != null)
    {
        if (componentRegistration.DependsOnValues.Any())
        {
            args.AddRange(componentRegistration.DependsOnValues);
        }
    }

    return Expression.Lambda(Expression.New(ctor, args.OrderBy(x => x.Position)
        .Select(x => x.Value).ToArray())).Compile();
}


private bool IsParamCoveredByManualRegistration(Type constructorOwnerType, ParameterInfo constructorArg)
{
    ComponentRegistration componentRegistration = FetchComponentRegistration(constructorOwnerType);
    if (!componentRegistration.HasManualConstructorParameters)
    {
        return false;
    }
    else
    {
        ConstructorParameterDependency constructorParameterDependency =
            componentRegistration.DependsOnValues.SingleOrDefault(
                x => x.ArgType == constructorArg.ParameterType && x.Name == constructorArg.Name);

        if (constructorParameterDependency != null)
        {
            constructorParameterDependency.Position = constructorArg.Position;
            return true;
        }
        else
        {
            return false;
        }
    }
}


private void CreateFactory(ComponentRegistration key, Delegate @delegate, InstanceMode instanceMode)
{
    IFactoryProvider factoryProvider = null;
    
    if (instanceMode == InstanceMode.Transient)
    {
        factoryProvider = new TransientFactory(@delegate);
    }
    
    if (instanceMode == InstanceMode.Singleton)
    {
        factoryProvider = new SingletonFactory(@delegate);
    }

    lock (syncLock)
    {
        components[key] = factoryProvider;
    }
}

实例解析的工作原理

解析实例可能是最简单的部分,因为所有繁重的工作应该已经由WireUp()方法完成,该方法的作用是确保为每个注册的Component创建IFactoryProvider。因此,从容器中解析一个对象实际上就是这3个简单的步骤:

  1. 找到具有正确 TypeToCreateComponentRegistration,作为Resolve<T>( )方法的所请求的泛型类型。
  2. 使用IFactoryProvider创建一个正确类型的实例(其中IFactoryProvider已由WireUp()方法创建),或者在找不到IFactoryProvider时抛出 Exception。如果遇到这种情况,可能是由于缺少或错误的ComponentRegistration
  3. 如果我们使用IFactoryProvider创建了一个新对象,只需遍历其带有 [Dependency]属性标记的属性,并从IOC容器中满足它们。完成后,只需返回该对象,该对象现在应该已经完成了以下操作:
    • IOC生成的构造函数参数应该已经得到满足。
    • 非IOC构造函数参数应该已经得到满足。
    • 标记为IOC依赖项的属性(带有 [Dependency]属性的属性)也应该得到满足。

这可以在下面的2个方法中看到:

public T Resolve<T>()
{
    lock (syncLock)
    {
        IFactoryProvider creator = components.Where(x => x.Key.TypeToCreate == typeof(T)).Select(x => x.Value).SingleOrDefault();

        if (creator != null)
        {
            T newlyCreatedObject = (T)creator.Create();
            SatisfyProperties<T>(newlyCreatedObject);
            return newlyCreatedObject;
        }
        else
        {
            throw new ContainerConfigurationException(string.Format(
                "Couldn't create instance of {0} could not find correct IFactoryProvider. This may be down to missing Component registration", 
                typeof(T).FullName));
        }
    }
}


private void SatisfyProperties<T>(T newlyCreatedObject)
{
    foreach (PropertyInfo prop in newlyCreatedObject.GetType().GetProperties()
        .Where(x => x.GetCustomAttributes(typeof(DependencyAttribute), false).Count() > 0))
    {
        IFactoryProvider factoryProvider = components.Single(x => x.Key.TypeToCreate == prop.PropertyType || 
            prop.PropertyType.IsAssignableFrom(x.Key.TypeToLookFor)).Value;

        if (factoryProvider != null)
        {
            prop.SetValue(newlyCreatedObject, factoryProvider.Create(), null);
        }
        else
        {
            throw new ContainerConfigurationException(string.Format(
                "Couldn't find instance of {0} to use for property injection", prop.PropertyType.FullName));
        }
    }
}

 

就这些

总之,就这些了。我写这篇东西纯属娱乐。如我所说,我真的不建议任何人使用它,因为市面上有一些非常优秀的IOC容器,我建议坚持使用其中一个。这个东西只是一个玩具,可以让你对如何构建一个简单的IOC容器获得一些见解,你可能无法使用第三方DLL,或者只是想要一些非常简单的东西。

同样,我再说一遍,我写这个纯粹是为了好玩。如果您想留下评论/投票,那将非常受欢迎。

© . All rights reserved.