很多 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. Ruby自学笔记(四)— 数组,Hash,正则表达式简介

    今天学习的是数组和Hash,数组在很多编程语言中都能常常用到. 数组: 数组是存储数据的一种容器,在Ruby中,数组中存储的数据可以是任何类型的数据:这和JAVA不同,在JAVA中,数组是存储同一类型 ...

  2. Ombrophobic Bovines

    poj2391:http://poj.org/problem?id=2391 题意:一个人有n个农场,每个农场都一个避雨的地方,每个农场有一些牛,每个避雨的地方能容纳牛的数量是有限的.农场之间有一些道 ...

  3. codeforces C. Valera and Tubes

    http://codeforces.com/contest/441/problem/C 题意:有n×m个方格,然后把这些方格分成k部分,每个部分内的方格的坐标满足|xi - xi + 1| + |yi ...

  4. ALTER TABLE

    •ALTER TABLE (表名) ADD (列名 数据类型); •ALTER TABLE (表名) MODIFY (列名 数据类型); •ALTER TABLE (表名) RENAME COLUMN ...

  5. linux下so动态库一些不为人知的秘密(转)

    linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些动态库, ...

  6. Strange Way to Express Integers (一般模线性方程组)

    Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 8476   Accepted: 2554 Description Elin ...

  7. 「Poetize5」水叮当的舞步

    Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变. 为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ 地毯上 ...

  8. 数据结构(Splay平衡树): [NOI2007] 项链工厂

    [NOI2007] 项链工厂 ★★★   输入文件:necklace.in   输出文件:necklace.out   简单对比 时间限制:4 s   内存限制:512 MB [问题描述] T公司是一 ...

  9. 【模拟】FOJ 2244 Daxia want to buy house

    题目链接: http://acm.fzu.edu.cn/problem.php?pid=2244 题目大意: 每月还款额=贷款本金×[月利率×(1+月利率)^还款月数]÷[(1+月利率)^还款月数-1 ...

  10. page-object使用(1)

    创建你的page 你必须做的第一件事情是创建你的page,这是一些包含了PageObject模块的简单的ruby类,请不要创建你自己的initialize方法,因为已经有一个存在而且不能被覆盖.如果你 ...