很多 Ioc 框架在创建对象的过程中,都会采取某种方式来缓存/复用/释放已构建的对象。在 My.Ioc 中,这个目的是通过 Lifetime/ILifetimeScope 来实现的。其中,Lifetime 实现了缓存/复用对象的功能,ILifetimeScope 则实现了复用/释放对象的功能。

My.Ioc 默认提供了三种 Lifetime:ContainerLifetime、TransientLifetime 和 ScopeLifetime。这里简单解释一下它们的含义:ContainerLifetime 继承自 SingletonLifetime,它实际上是一种单例模式的实现。TransientLifetime 顾名思义,即每次请求都新建一个对象返回给调用者。ScopeLifetime 则表明在某个 scope 及其父 scope 中创建的对象将在该 scope 内复用。

上面这样解释也许大家不是很容易明白,下面我们结合示例代码来说明:

using System;
using System.Diagnostics;
using My.Ioc;
using My.Ioc.Exceptions; namespace LifetimeAndLifetimeScope
{
#region Test Types public class SingletonDisposableClass : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposing SingletonDisposableClass...");
}
}
public class SingletonNonDisposableClass { } public class TransientNonDisposableClass { }
public class TransientDisposableClass : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposing TransientDisposableClass...");
}
} public class ScopedDisposableClass : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposing ScopedDisposableClass...");
}
}
public class ScopedNonDisposableClass
{
} #endregion class Program
{
static void Main(string[] args)
{
var container = new ObjectContainer(false);
Register(container); ResolveTransient(container);
ResolveScope(container);
ResolveSingleton(container); // Dispose the container
// The disposable singleton instances should be disposed here.
container.Dispose();
Console.ReadLine();
} static void Register(IObjectContainer container)
{
container.Register<TransientDisposableClass>()
.In(Lifetime.Transient()); // This line can be omitted
container.Register<TransientNonDisposableClass>()
.In(Lifetime.Transient()); // This line can be omitted container.Register<ScopedDisposableClass>()
.In(Lifetime.Scope());
container.Register<ScopedNonDisposableClass>()
.In(Lifetime.Scope()); container.Register<SingletonDisposableClass>()
.In(Lifetime.Container()); //Singleton
container.Register<SingletonNonDisposableClass>()
.In(Lifetime.Container()); //Singleton container.CommitRegistrations();
} static void ResolveTransient(IObjectContainer container)
{
var nonDisposable1 = container.Resolve<TransientNonDisposableClass>();
var nonDisposable2 = container.Resolve<TransientNonDisposableClass>();
Debug.Assert(nonDisposable1 != nonDisposable2, "nonDisposable1 == nonDisposable2"); try
{
var disposable_Error = container.Resolve<TransientDisposableClass>();
}
catch (Exception ex)
{
Debug.Assert(ex is InvalidLifetimeScopeException);
} using (var scope = container.BeginLifetimeScope())
{
var disposable1 = container.Resolve<TransientDisposableClass>();
var disposable2 = container.Resolve<TransientDisposableClass>();
Debug.Assert(disposable1 != disposable2, "disposable1 == disposable2");
}
} static void ResolveScope(IObjectContainer container)
{
try
{
var nondisposable_Error = container.Resolve<ScopedNonDisposableClass>();
}
catch (Exception ex)
{
Debug.Assert(ex is InvalidLifetimeScopeException);
} string nested_scope_should_share_instance =
"{0} should be the same to {1}, because the {0} is resolved in the outer scope, "
+ "which is shared with the scope where {1} is resolved, so they must be a same instance."; string same_scope_should_share_instance =
"{0} should be the same to {1}, because they are resolved in the same scope."; using (var scope1 = container.BeginLifetimeScope())
{
var disposable1 = scope1.Resolve<ScopedDisposableClass>();
var nonDisposable1 = scope1.Resolve<ScopedNonDisposableClass>(); using (var scope2 = scope1.BeginLifetimeScope())
{
var disposable2 = scope2.Resolve<ScopedDisposableClass>();
var disposable3 = scope2.Resolve<ScopedDisposableClass>();
Debug.Assert(disposable1 == disposable2,
string.Format(nested_scope_should_share_instance, "disposable1", "disposable2"));
Debug.Assert(disposable1 == disposable3,
string.Format(nested_scope_should_share_instance, "disposable1", "disposable3"));
Debug.Assert(disposable2 == disposable3,
string.Format(same_scope_should_share_instance, "disposable2", "disposable3")); var nonDisposable2 = scope1.Resolve<ScopedNonDisposableClass>();
var nonDisposable3 = scope1.Resolve<ScopedNonDisposableClass>();
Debug.Assert(nonDisposable1 == nonDisposable2,
string.Format(nested_scope_should_share_instance, "nonDisposable1", "nonDisposable2"));
Debug.Assert(nonDisposable1 == nonDisposable3,
string.Format(nested_scope_should_share_instance, "nonDisposable1", "nonDisposable3"));
Debug.Assert(nonDisposable2 == nonDisposable3,
string.Format(same_scope_should_share_instance, "nonDisposable2", "nonDisposable3"));
}
}
} static void ResolveSingleton(IObjectContainer container)
{
var disposable1 = container.Resolve<SingletonDisposableClass>();
var disposable2 = container.Resolve<SingletonDisposableClass>();
Debug.Assert(disposable1 == disposable2, "disposable1 != disposable2"); var nonDisposable1 = container.Resolve<SingletonDisposableClass>();
var nonDisposable2 = container.Resolve<SingletonDisposableClass>();
Debug.Assert(nonDisposable1 == nonDisposable2, "disposable1 != nonDisposable2");
}
}
}

在示例中,我们设计了这么三对类型:SingletonDisposableClass/SingletonNonDisposableClass、ScopedDisposableClass/ScopedNonDisposableClass 以及 TransientNonDisposableClass/TransientDisposableClass。用意很简单,分别注册到上面三种类型的 Lifetime 中。而每一类对象之所以有 Disposable 和 NonDisposable 两个类型,是因为我们要展示这三类 Lifetime 中对象清理的策略。

我们首先在 Register 方法中将 SingletonDisposableClass/SingletonNonDisposableClass 注册为 Container 生命周期,将 ScopedDisposableClass/ScopedNonDisposableClass 注册为 Scope 生命周期,并将 TransientNonDisposableClass/TransientDisposableClass 注册为 Transient 生命周期。

做好了准备工作之后,下面我们要让容器来为我们创建上述对象。首先,我们看 ResolveTransient 这个方法。这个方法旨在告诉我们:

  1. 如果对象被注册为 Transient 生命周期,那么当我们请求容器解析对象时,无论我们是否使用 ILifetimeScope,每次返回给我们的都是容器新创建的对象。也就是说,两次解析返回的结果不是同一个对象。
  2. 如果对象实现了 IDisposable 接口,那么在解析该对象时,就必须先向容器请求一个 ILifetimeScope,然后在该 ILifetimeScope 中解析对象。否则,您将会收到一个 InvalidLifetimeScopeException 的异常。而且,在 ILifetimeScope 结束 (Dispose) 时,所请求的对象也会随之被清理 (Dispose)。

接着,我们来看一下 ResolveScope 这个方法。这个方法是我们用来演示 ScopeLifetime 的运行方式的。它告诉我们:

  1. 当对象被注册为 Scope 生命周期时,无论其是否实现 IDisposable 接口,在解析时我们都必须先向容器请求一个 ILifetimeScope,然后在该 ILifetimeScope 中解析对象。否则,您将会收到一个 InvalidLifetimeScopeException 的异常。
  2. 只有实现了 IDisposable 接口的对象才会在解析后,随着自身所处的 ILifetimeScope 的结束而被清理 (Dispose)。
  3. ILifetimeScope 是可以嵌套的。例如,在示例代码中,scope2 是嵌套在 scope1 中的。在这种情况下,我们将 scope1 称为外层(父) scope,将 scope2 称为内层(子) scope。
  4. 内层 scope 可以共享外层 scope 的对象,所以我们才会看到示例代码中有 [disposable1 == disposable2]、[nonDisposable1 == nonDisposable2] 等那么多相等性断言。

最后我们来看 ResolveSingleton 这个方法。我们使用这个方法来观察 ContainerLifetime 的运行方式。这个方法的运行结果告诉我们:

  1. 如果对象被注册为 Container 生命周期,那么当我们请求容器解析对象时,无论该对象是否实现了 IDisposable 接口,都不需要使用事先向容器请求 ILifetimeScope。但当然,要使用 ILifetimeScope 来解析它们也是可以的,最后结果应该都是返回相同实例。
  2. 如果所解析的对象实现了 IDisposable 接口,那么它要一直等到容器 (IObjectContainer) 本身的生命周期结束之后才被清理。

本文源码可在此处下载,压缩包中包含了 My.Ioc 框架的源码和本示例以及其他一些示例的源码。

My.Ioc 代码示例——Lifetime 和 ILifetimeScope的更多相关文章

  1. My.Ioc 代码示例——如何使用默认构造参数,以及如何覆盖默认构造参数

    在 Ioc 世界中,有些框架(例如 Autofac/NInject/Unity)支持传递默认参数,有些框架(例如 SimpleInjector/LightInjector 等)则不支持.作为 My.I ...

  2. My.Ioc 代码示例——实现自动注册/解析

    在很多 Ioc 容器中,当使用者向容器请求实现了某个契约类型 (Contract Type) 的服务时 (调用类似如下方法 container.Resolve(Type contractType)), ...

  3. My.Ioc 代码示例——使用观察者机制捕获注册项状态的变化

    在 My.Ioc 中,要想在服务注销/注册时获得通知,可以通过订阅 ObjectBuilderRegistered 和 ObjectBuilderUnregistering 这两个事件来实现.但是,使 ...

  4. My.Ioc 代码示例——谈一谈如何实现装饰器模式,兼谈如何扩展 My.Ioc

    装饰器模式体现了一种“组合优于继承”的思想.当我们要动态为对象增加新功能时,装饰器模式往往是我们的好帮手. 很多后期出现的 Ioc 容器都为装饰器模式提供了支持,比如说 Autofac.在 My.Io ...

  5. My.Ioc 代码示例——属性和方法注入

    在 My.Ioc 中,我们可以指定让容器在构建好对象实例之后,自动为我们调用对象的公共方法或是为对象的公共属性赋值.在解析对象实例时,容器将根据我们在注册对象时指定的方法调用或属性赋值的先后顺序,调用 ...

  6. My.Ioc 代码示例——使用条件绑定和元数据(可选)构建插件树

    本文旨在通过创建一棵插件树来演示条件绑定和元数据的用法. 说“插件树”也许不大妥当,因为在一般观念中,谈到插件树,我们很容易会想到 Winform/Wpf 中的菜单.举例来说,如果要在 Winform ...

  7. My.Ioc 代码示例——避免循环依赖

    本文的目的在于通过一些示例,向大家说明 My.Ioc 支持哪些类型的依赖关系.也就是说,如何设计对象不会导致循环依赖. 在 Ioc 世界中,循环依赖是一个顽敌.这不仅因为它会导致 Ioc 容器抛出异常 ...

  8. My.Ioc 代码示例——利用 ObjectBuilderRequested 事件实现延迟注册

    在使用 Ioc 框架时,一般我们建议集中在一个称为 Composition Root(其含义请参见下面的小注)的位置来注册 (Register) 和解析 (Resolve) 服务.这种做法的目的在于限 ...

  9. My.Ioc 代码示例——注册项的注销和更新

    当您需要从 Ioc 容器中注销/删除一个注册项的时候,您会怎么做呢? 有人曾经在 stackoverflow 上提问“如何从 Unity 中注销一个注册项”.对于这个问题,有人的回答是“有趣.你为什么 ...

随机推荐

  1. OC面向对象的三大特征

    OC面向对象的三大特征 1.OC面向对象的三大特封装 1)封装:完整的说是成员变量的封装. 2)在成语方法里面的成员变量最好不要使用@public这样会直接暴露在外面被别人随随便便修改,封装的方法还可 ...

  2. 字符串处理---统计每一行字符串当中的字符“u”个数

    package com.guoxiaoming.string; import java.io.BufferedReader; import java.io.FileInputStream; impor ...

  3. 关于MYSQL优化(持续更新)

    *利用MYSQL数据缓存提高效率,注意事项: 1.应用环境:不经常改变的表及对此表相同的查询 2.不适用于服务器端编写的语句 3.根据数据使用频率,合理分解表 4.合理使用默认条件,提高命中率 5.统 ...

  4. mysql 修改max_allowed_packet

    -- 查询max_allowed_packetshow VARIABLES like '%max_allowed_packet%'; 修改 my.ini 或 my.cnf [mysqld] max_a ...

  5. 如何在js文件中实现获取request.getCotextPath();

    我们在jsp中可以方便的使用“request.getCotext()”来获取工程的根目录. 但是如果我们的js代码存在一个单独的js文件中,这时候再想获取根目录,我们就要自己截取了.可以采用下面的方式 ...

  6. GC的前世与今生

    GC的前世与今生 虽然本文是以.net作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...

  7. SQLPLUS使用

    1.CMD命令 2.输入SQLPLUS 3.如果oracle服务器中装有多个数据库实例,则在用户名处输入:用户名/密码@数据库名称.如果数据库服务器不在本机上,还需要加上数据库服务器的地址:用户名/密 ...

  8. Arrays常用API的事例

    import java.util.ArrayList;import java.util.Arrays;import java.util.List; public class TestArrays { ...

  9. ♫【Underscore.js】

    Underscore.js Underscore.js 1.5.2 中文文档 Underscore 是一个JavaScript实用库,提供了类似Prototype.js (或 Ruby)的一些功能,但 ...

  10. Andriod视频http://pan.baidu.com/share/link?shareid=7300&uk=3339495714

    老罗Android开发 视频教程           一.Android入门介绍 视频教程     1.1 android系统介绍   1.3 如何搭建android开发环境   1.5 androi ...