很多 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. mysql 修改max_allowed_packet

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

  2. DaoImpl中实现查询分页-使用HibernateCallback来做更加方便

    /** * */ package com.wolfgang.dao; import java.sql.SQLException; import java.util.List; import org.h ...

  3. BZOJ 1037 生日聚会

    Description 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party. hidadz带着朋友们来到花园中,打算坐成一排玩游戏.为了游戏不至于无聊,就座的方案应满足如下条件: ...

  4. IE8一枝独秀的JS兼容BUG

    // 例如淡入淡出的封装类文件 function ImagesEff(div,time){ this.arr=[];//装载所有div this.time=time; this.recordOld=n ...

  5. 以Facebook为案例剖析科技公司应有的工具文化

    http://www.36kr.com/p/146507.html 编者按:本文由 @王淮Harry哥 撰写,摘自他即将出版的新书.王淮是 Facebook 早期员工,中国藉第二位工程师第一位研发经理 ...

  6. 【线段树】HDU 5493 Queue (2015 ACM/ICPC Asia Regional Hefei Online)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5493 题目大意: N个人,每个人有一个唯一的高度h,还有一个排名r,表示它前面或后面比它高的人的个数 ...

  7. Unique Paths ——LeetCode

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  8. FTP服务器中文环境引起润日下载不了附件问题解析

    20160229日某农商行因为FTP下载功能有问题,导致当天所有涉及FTP文件下载的交易都不能正常使用,对于银行来说影响还是比较大.现将当天出问题的原因及处理过程解析如下,忘能给碰到类似问题的同行以供 ...

  9. spj 设计

    在数据库中使用SELECT INTO 语句从SP.J.P表中创建一个新表J_P_SPJ新表J_P_QTY中的属性列包括:工程号 (JNO).工程名(JNAME).零件号(PNO).零件名(PNAME) ...

  10. 跨平台通信中间件thrift学习【Java版本】(转)

    转自:http://neoremind.com/2012/03/%E8%B7%A8%E5%B9%B3%E5%8F%B0%E9%80%9A%E4%BF%A1%E4%B8%AD%E9%97%B4%E4%B ...