IOC注入框架——Unity中的BuildUp与LifetimeManagers
2012-10-27 22:30 C#与.NET探索者 阅读(786) 评论(0) 编辑 收藏 举报一、使用BuildUp向已存在的对象进行依赖注入
准备工作:编写下面的几个类
public class Owner
{
public string Name
{
get
{
return "Inrie";
}
}
public int Age
{
get
{
return 24;
}
}
}
public abstract class Player
{
public abstract void Play();
public abstract string Name { get; }
[Dependency]
public Owner Owner { get; set; }
}
public class Mp3Player : Player
{
public override void Play()
{
Console.WriteLine("Playing Mp3");
}
public override string Name
{
get
{
return "Mp3 Player";
}
}
}
public class CDPlayer : Player
{
public override void Play()
{
Console.WriteLine("Playing CD");
}
public override string Name
{
get
{
return "CD Player";
}
}
}
public class DVDPlayer : Player
{
public override void Play()
{
Console.WriteLine("Playing DVD");
}
public override string Name
{
get
{
return "DVD Player";
}
}
}
public class TempBuildUp
{
public TempBuildUp()
{
}
}
public class CoreLib
{
public Player CreatePlayer()
{
return new Mp3Player();
}
}
按照我们上篇文章说的依赖注入,我们看到在MP3Player这个类中的Name属性依赖于Owner类,因此在编写代码的时候我们需要使用UnityContainer进行依赖注入。
如:
IUnityContainer container = new UnityContainer();
container.RegisterType<Player, Mp3Player>();
Player player = container.Resolve<Player>();
Response.Write(player.Owner.Name);
但是如果我把代码改为如下方式:
Player player = new Mp3Player();
Response.Write(player.Owner.Name);
代码会抛出异常("未将对象引用设置到对象实例"),这是什么原因呢?
因为,我们没有使用UnityContainer容器实例化Player,如果目标对象并非由容器实例化(非 Resolve 创建),那么依赖注入行为是不会发生的。(车延禄)
除了使用UnitContainer对象的Resolve方法实例化对象,我们还可以使用BuildUp进行操作,BuildUp就是对已存在的对象进行依赖注入。
如:
IUnityContainer container = new UnityContainer();
Player player = new Mp3Player(); //未实现依赖的Player对象
Player p = container.BuildUp<Player>(player); //对player对象进行依赖注入
Response.Write(p.Owner.Name); //执行成功。
下面再看一个例子:
通过CoreLib类来创建Player对象,然后显示该Player对象的Owner.Name属性。
现在出现了一个新的问题:Player对象的创建过程在Corelib的CreatePlayer方法中执行的,该方法只是简单地生成MP3Player对象,并没有对它进行依赖注入,即意味着,CoreLib.CreatePlayer()生成的Player对象的Owner引用是Null。
所以如果客户码这样编写就错了:
CoreLib lib = new CoreLib();
Player player = lib.CreatePlayer();
Response.Write(player.Owner.Name);
聪明的朋友一眼能看出来,这里没有使用依赖注入。但由于Player对象的生成在CoreLib中实现,并不在我们客户代码的控制范围之内,因此我们无法使用Resolve在客户代码中通过CoreLib对象创建Player对象实例。
在这种情况下,我们可以使用Container的BuildUp()方法,在CoreLib对象中动态进行依赖注入。
代码如下:
IUnityContainer container = new UnityContainer();
CoreLib lib = new CoreLib(); //生成CoreLib对象
Player p = lib.CreatePlayer(); //通过CoreLib对象生成Player对象,但此时p还是个“半成品”
Player player = container.BuildUp<Player>(p); //对“半成品”p进行依赖注入
Response.Write(player.Owner.Name); //执行成功
二、Lifetime Managers 管理对象的生命周期
Lifetime Managers控制怎样存放到对象实例的引用和容器怎样销毁这些实例,也就是说Unity基于具体的Lifetime Manager 类去管理对象的创建和销毁。
目前Unity中提供两个Lifetime Manager类可供我们直接使用,当然你也可以实现自己的Lifetime Manager类。
A. ContainerControlledLifetimeManager
Unity保存一个指向对象实例的引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例。也就是说实现了对象单例模式。默认情况下,RegisterInstance方法使用该Lifetime Manager。
B. ExternallyControlledLifetimeManager
Unity仅保存一个指向对象实例的弱引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例,也是单例模式。但是由于当对象创建完之后,容器没有对该对象的强引用,所以就可能出现当其他地方没有去强引用它时候,会被GC回收掉。
1.RegisterType
当用RegisterType注册映射关系时,如果没有指定LifetimeManager,默认是使用一个瞬态的Lifetime Manager。即每次通过Unity容器获取对象的实例时都会重新创建一个该实例,也就是说Unity容器不存在一个到该对象的引用。
如:
IUnityContainer container = new UnityContainer();
container.RegisterType<IPlayer, Mp3Player>();
IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}",player1.GetHashCode()));
IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}",player2.GetHashCode()));
代码执行结果:
通过输出的player1和player2对象的HashCode值可以看出,player1和player2分别是Mp3Player类的不同实例。
要实现单例模式,通过在RegisterType时为它指定相应的Lifetime Manager可以实现单例模式,从上面对ContainerControlledLifetimeManager和ExternallyControlledLifetimeManager的介绍可以知道,这两个Lifetime Manager都可以支持单例模式。
如:
IUnityContainer container = new UnityContainer();
//这里指定使用ContainerControlledLifetimeManager对象
container.RegisterType<IPlayer, Mp3Player>(new ContainerControlledLifetimeManager());
IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}",player1.GetHashCode()));
IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}",player2.GetHashCode()));
执行结果
通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。
2. RegisterInstance
当用RegisterInstance注册映射关系时,如果没有指定Lifetime Manager,默认是使用ContainerControlledLifetimeManager,即支持单例模式。
如:(车延禄)
IUnityContainer container = new UnityContainer();
IPlayer mp3Player = new Mp3Player();
container.RegisterInstance<IPlayer>(mp3Player);
IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}", player1.GetHashCode()));
IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}", player2.GetHashCode()));
代码执行结:
通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。