My.Ioc 代码示例——Lifetime 和 ILifetimeScope
很多 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 这个方法。这个方法旨在告诉我们:
- 如果对象被注册为 Transient 生命周期,那么当我们请求容器解析对象时,无论我们是否使用 ILifetimeScope,每次返回给我们的都是容器新创建的对象。也就是说,两次解析返回的结果不是同一个对象。
- 如果对象实现了 IDisposable 接口,那么在解析该对象时,就必须先向容器请求一个 ILifetimeScope,然后在该 ILifetimeScope 中解析对象。否则,您将会收到一个 InvalidLifetimeScopeException 的异常。而且,在 ILifetimeScope 结束 (Dispose) 时,所请求的对象也会随之被清理 (Dispose)。
接着,我们来看一下 ResolveScope 这个方法。这个方法是我们用来演示 ScopeLifetime 的运行方式的。它告诉我们:
- 当对象被注册为 Scope 生命周期时,无论其是否实现 IDisposable 接口,在解析时我们都必须先向容器请求一个 ILifetimeScope,然后在该 ILifetimeScope 中解析对象。否则,您将会收到一个 InvalidLifetimeScopeException 的异常。
- 只有实现了 IDisposable 接口的对象才会在解析后,随着自身所处的 ILifetimeScope 的结束而被清理 (Dispose)。
- ILifetimeScope 是可以嵌套的。例如,在示例代码中,scope2 是嵌套在 scope1 中的。在这种情况下,我们将 scope1 称为外层(父) scope,将 scope2 称为内层(子) scope。
- 内层 scope 可以共享外层 scope 的对象,所以我们才会看到示例代码中有 [disposable1 == disposable2]、[nonDisposable1 == nonDisposable2] 等那么多相等性断言。
最后我们来看 ResolveSingleton 这个方法。我们使用这个方法来观察 ContainerLifetime 的运行方式。这个方法的运行结果告诉我们:
- 如果对象被注册为 Container 生命周期,那么当我们请求容器解析对象时,无论该对象是否实现了 IDisposable 接口,都不需要使用事先向容器请求 ILifetimeScope。但当然,要使用 ILifetimeScope 来解析它们也是可以的,最后结果应该都是返回相同实例。
- 如果所解析的对象实现了 IDisposable 接口,那么它要一直等到容器 (IObjectContainer) 本身的生命周期结束之后才被清理。
本文源码可在此处下载,压缩包中包含了 My.Ioc 框架的源码和本示例以及其他一些示例的源码。
My.Ioc 代码示例——Lifetime 和 ILifetimeScope的更多相关文章
- My.Ioc 代码示例——如何使用默认构造参数,以及如何覆盖默认构造参数
在 Ioc 世界中,有些框架(例如 Autofac/NInject/Unity)支持传递默认参数,有些框架(例如 SimpleInjector/LightInjector 等)则不支持.作为 My.I ...
- My.Ioc 代码示例——实现自动注册/解析
在很多 Ioc 容器中,当使用者向容器请求实现了某个契约类型 (Contract Type) 的服务时 (调用类似如下方法 container.Resolve(Type contractType)), ...
- My.Ioc 代码示例——使用观察者机制捕获注册项状态的变化
在 My.Ioc 中,要想在服务注销/注册时获得通知,可以通过订阅 ObjectBuilderRegistered 和 ObjectBuilderUnregistering 这两个事件来实现.但是,使 ...
- My.Ioc 代码示例——谈一谈如何实现装饰器模式,兼谈如何扩展 My.Ioc
装饰器模式体现了一种“组合优于继承”的思想.当我们要动态为对象增加新功能时,装饰器模式往往是我们的好帮手. 很多后期出现的 Ioc 容器都为装饰器模式提供了支持,比如说 Autofac.在 My.Io ...
- My.Ioc 代码示例——属性和方法注入
在 My.Ioc 中,我们可以指定让容器在构建好对象实例之后,自动为我们调用对象的公共方法或是为对象的公共属性赋值.在解析对象实例时,容器将根据我们在注册对象时指定的方法调用或属性赋值的先后顺序,调用 ...
- My.Ioc 代码示例——使用条件绑定和元数据(可选)构建插件树
本文旨在通过创建一棵插件树来演示条件绑定和元数据的用法. 说“插件树”也许不大妥当,因为在一般观念中,谈到插件树,我们很容易会想到 Winform/Wpf 中的菜单.举例来说,如果要在 Winform ...
- My.Ioc 代码示例——避免循环依赖
本文的目的在于通过一些示例,向大家说明 My.Ioc 支持哪些类型的依赖关系.也就是说,如何设计对象不会导致循环依赖. 在 Ioc 世界中,循环依赖是一个顽敌.这不仅因为它会导致 Ioc 容器抛出异常 ...
- My.Ioc 代码示例——利用 ObjectBuilderRequested 事件实现延迟注册
在使用 Ioc 框架时,一般我们建议集中在一个称为 Composition Root(其含义请参见下面的小注)的位置来注册 (Register) 和解析 (Resolve) 服务.这种做法的目的在于限 ...
- My.Ioc 代码示例——注册项的注销和更新
当您需要从 Ioc 容器中注销/删除一个注册项的时候,您会怎么做呢? 有人曾经在 stackoverflow 上提问“如何从 Unity 中注销一个注册项”.对于这个问题,有人的回答是“有趣.你为什么 ...
随机推荐
- Java线程生命模型
一. 线程状态类型1. 新建状态(New):新创建了一个线程对象.2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运 ...
- [151225] Python3 实现最大堆、堆排序,解决TopK问题
参考资料: 1.算法导论,第6章,堆排序 堆排序学习笔记及堆排序算法的python实现 - 51CTO博客 堆排序 Heap Sort - cnblogs 小根堆实现优先队列:Python实现 -cn ...
- Unity问答——请教一下NGUI的图片转换问题
这篇博客源自我在泰课在线的回答.链接:http://www.taikr.com/group/1/thread/92 问:请教一下NGUI的图片转换问题 1.NGUI能不能把导入的方形图片转成圆形的(因 ...
- PLSQL 看连接数据库的用户
1. PLSQL选用SYSDBA登录,用户sys,密码xxx 2. 登录后,点tools菜单有sessions子菜单, 3.点击sessions子菜单,可以看到连接用户
- SharePoint REST api
http://msdn.microsoft.com/en-us/magazine/dn198245.aspx Understanding and Using the SharePoint 2013 R ...
- codeforces C. Ryouko's Memory Note
题意:给你m个数,然后你选择一个数替换成别的数,使得.最小.注意选择的那个数在这m个数与它相同的数都必须替换同样的数. 思路:用vector记录每一个数与它相邻的数,如果相同不必记录,然后遍历替换成与 ...
- cf C On Number of Decompositions into Multipliers
题意:给你n个数,然后把这个n个数的乘积化成n个数相乘,可以化成多少个. 思路:分解质因数,求出每一个质因子的个数,然后用组合数学中隔板法把这些质因子分成n分,答案就是所有质因子划分成n份的情况的乘积 ...
- 子类化窗口控件的窗口过程(系统级替换,与直接替换控件的WndProc方法不是一回事)
要说灵活性,自然是比不上Delphi自带的覆盖WndProc,或者替换WndProc方法. unit Unit1; interface uses Windows, Messages, SysUtils ...
- Lesson 6: CronTrigger
CronTrigger is often more useful than SimpleTrigger, if you need a job-firing schedule that recurs b ...
- Spark运行各个时间段的解释
package org.apache.spark.ui private[spark] object ToolTips { val SCHEDULER_DELAY = ""& ...