很多 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. HTML/W3C-WHATWG-Differences

    Differences between the W3C HTML 5.1 specification and the WHATWG LS The W3C HTML 5.1 specification: ...

  2. 【Java】Servlet 工作原理解析

    Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.因而掌握 Servlet 的工作原理是成为一名合格的 Java Web 技术开发人员的 ...

  3. Linux 统计文件夹下文件个数

    查看统计当前目录下文件的个数,包括子目录里的. ls -lR| grep "^-" | wc -l Linux下查看某个目录下的文件.或文件夹个数用到3个命令:ls列目录.用gre ...

  4. java学习之关键字

    java语言当中的关键字,之所以存在,是为了告诉编译器如何解释一段有意义的代码段.比如说 /**需求:演示java中关键字存在的含义步骤:用class,public,static,void等说明什么是 ...

  5. Unity 技能按钮触发特效

    unity 版本:4.5.1 NGUI版本:3.6.5 首先,要导入特效资源包,导入应该是基本中的基础,但是对于初学者来说好像很少有这方面的介绍,也许是我现学现用书看的不够认真,因为导入这个问题卡了好 ...

  6. BZOJ1589: [Usaco2008 Dec]Trick or Treat on the Farm 采集糖果

    1589: [Usaco2008 Dec]Trick or Treat on the Farm 采集糖果 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 4 ...

  7. 数据结构(trie,启发式合并):HDU 5841 Alice and Bob

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABJEAAAE6CAIAAAApz1RvAAAgAElEQVR4nO3d3css1b3g8fyTdbHJbD

  8. 高等数学(拉格朗日乘子法):NOI 2012 骑行川藏

    [NOI2012] 骑行川藏 输入文件:bicycling.in   输出文件:bicycling.out   评测插件 时间限制:1 s   内存限制:128 MB NOI2012 Day1 Des ...

  9. 加载GIF动画方法 iOS

    方法一 使用UIWebView _codeStr为gif网址      如果是本地的gif可以直接使用dataWithContentsOfFile方法 NSData *data = [NSData d ...

  10. string和stringbuffer stringbuilder的快速理解。

    这三个对象都可操作字符串,区别string定义的变量除非重新赋值,否则是不可改变的.调用string的方法不会改变,但是其他两个有对象的方法可改变,比如apend的方法,后两个区别一个是线程安全不安全 ...