RegisterFactory(InjectionFactory)的应用
Unity是一个.NET Framework用于依赖注入的容器。依赖注入一般分为三个阶段:Register, Resolve和Dispose。Unity支持三种方式的Register:
- Instance registration
- Type registration
- Factory registration
那这三种方式分别用于什么场景呢?简单来说,Instance registration和Type
registration较为常见,直接通过ResolvedParameter解析依赖的对象,Factory
registration更灵活,可以通过调用方法创建对象。举个例子,如果注入对象所依赖的参数是一个List<T>
或Dictionary<T1, T2>
,而T又是自定义的类,那么就必须使用Factory
registration。本文主要介绍的就是如何解析一个自定义类的List或Dictionary。
RegisterType示例
从例子切入,定义IAnimal Interface,并有Tiger和Deer两个实现,还有一个Zoo类,聚合了Tiger和Deer:
public interface IAnimal
{
void Run();
}
public class Tiger : IAnimal
{
public void Run()
{
Console.WriteLine("Tiger runs away");
}
}
public class Deer : IAnimal
{
public void Run()
{
Console.WriteLine("Deer runs away");
}
}
public class Zoo
{
private readonly IAnimal _tiger;
private readonly IAnimal _deer;
public Zoo(IAnimal tiger, IAnimal deer)
{
_tiger = tiger;
_deer = deer;
}
public void Play()
{
_tiger.Run();
_deer.Run();
}
}
注入并调用:
class Program
{
static void Main(string[] args)
{
// Registration
var container = new UnityContainer();
container.RegisterType<IAnimal, Tiger>("Tiger", new InjectionConstructor());
container.RegisterType<IAnimal, Deer>("Deer", new InjectionConstructor());
container.RegisterType<Zoo, Zoo>("Zoo", new InjectionConstructor(
new ResolvedParameter<IAnimal>("Tiger"),
new ResolvedParameter<IAnimal>("Deer")));
// Resolve the instances
var zoo = container.Resolve<Zoo>("Zoo");
zoo.Play();
Console.ReadKey();
}
}
运行结果:
Tiger runs away
Deer runs away
RegisterFactory示例
RegisterFactory (已淘汰的InjectionFactory)可以用Unity创建的Factory创建对象,这是它与RegisterInstance和RegisterType的主要区别。
继续上面的例子,为了更灵活,考虑让Zoo类接受animal list作为参数:
public class Zoo
{
private readonly List<IAnimal> _animals;
public Zoo(List<IAnimal> animals)
{
_animals = animals;
}
public void Play()
{
foreach (var animal in _animals)
{
animal.Run();
}
}
}
那么如何注入Listcontainer.RegisterType<List<IAnimal>>(
"AnimalList",
new InjectionConstructor(
new List<IAnimal>
{
new ResolvedParameter<IAnimal>("Tiger"),
new ResolvedParameter<IAnimal>("Deer")
})
);
遇到编译错误:
Argument 1: cannot convert from 'Unity.Injection.ResolvedParameter<ConsoleApp1.IAnimal>' to 'ConsoleApp1.IAnimal'
报错原因在于List中是IAnimal类型,而ResolvedParameter
RegisterFactory可以用Factory通过执行一段代码解决问题:
container.RegisterFactory<List<IAnimal>>(
"AnimalList",
m => new List<IAnimal>
{
m.Resolve<IAnimal>("Tiger"),
m.Resolve<IAnimal>("Deer"),
}
);
RegisterFactory接受Func<IUnityContainer, object>
为一个Factory,在List初始化时Resolve两个IAnimal对象。
直接解析自定义类Collection
不用RegisterFactory也可直接解析自定义类Collection,但有两个条件:
- 待解析的Collection必须是array
[]或
IEnumerable<T>
- 所有被注册为该自定义类的类型都会被加入Collection中,不能解析出所有自定义类的一个子集。听起来很绕,可以看下面实例。
先修改Zoo的参数,由List<IAnimal>
变为IEnumerable<IAnimal>
public class Zoo
{
private readonly IEnumerable<IAnimal> _animals;
public Zoo(IEnumerable<IAnimal> animals)
{
_animals = animals;
}
public void Play()
{
foreach (var animal in _animals)
{
animal.Run();
}
}
}
然后可通过下面语句注册Zoo:
container.RegisterType<Zoo, Zoo>("Zoo",
new InjectionConstructor(
new ResolvedParameter<List<IAnimal>>()
));
但这种注册方式会把所有已注册的IAnimal都加入List<IAnimal>
,如果只想把Tiger加入其中则不行,必须使用RegisterFactory的方式。
RegisterFactory vs RegisterType
考虑Zoo(IAnimal tiger, IAnimal deer)的两种不同注入方式:
container.RegisterType<Zoo, Zoo>("Zoo", new InjectionConstructor(
new ResolvedParameter<IAnimal>("Tiger"),
new ResolvedParameter<IAnimal>("Deer")));
container.RegisterFactory<Zoo>("Zoo",
m => new Zoo(
m.Resolve<IAnimal>("Tiger"),
m.Resolve<IAnimal>("Deer"))
);
在container.Resolve
简而言之,RegisterFactory是一种比RegisterType更为灵活的注入方式,可以通过注入一个Factory的委托执行代码进行对象创建,但RegisterFactory用起来更为复杂。
完整代码
using System;
using System.Collections.Generic;
using Unity;
using Unity.Injection;
namespace ConsoleApp1
{
public interface IAnimal
{
void Run();
}
public class Tiger : IAnimal
{
public void Run()
{
Console.WriteLine("Tiger runs away");
}
}
public class Deer : IAnimal
{
public void Run()
{
Console.WriteLine("Deer runs away");
}
}
public class Zoo
{
private readonly IEnumerable<IAnimal> _animals;
public Zoo(IEnumerable<IAnimal> animals)
{
_animals = animals;
}
public void Play()
{
foreach (var animal in _animals)
{
animal.Run();
}
}
}
public class SimpleZoo
{
private readonly IEnumerable<IAnimal> _animals;
public SimpleZoo(IEnumerable<IAnimal> animals)
{
_animals = animals;
}
public void Play()
{
foreach (var animal in _animals)
{
animal.Run();
}
}
}
class Program
{
static void Main(string[] args)
{
// Registration
var container = new UnityContainer();
container.RegisterType<IAnimal, Deer>("Deer", new InjectionConstructor());
container.RegisterType<IAnimal, Tiger>("Tiger", new InjectionConstructor());
container.RegisterFactory<List<IAnimal>>(
"AnimalList",
m => new List<IAnimal>
{
m.Resolve<IAnimal>("Tiger")
}
);
container.RegisterType<Zoo, Zoo>("Zoo",
new InjectionConstructor(
new ResolvedParameter<List<IAnimal>>()
));
container.RegisterType<SimpleZoo, SimpleZoo>("SimpleZoo",
new InjectionConstructor(
new ResolvedParameter<List<IAnimal>>("AnimalList")
));
// Resolve the instances
Console.WriteLine("Zoo: ");
var zoo = container.Resolve<Zoo>("Zoo");
zoo.Play();
Console.WriteLine();
Console.WriteLine("SimpleZoo: ");
var simpleZoo = container.Resolve<SimpleZoo>("SimpleZoo");
simpleZoo.Play();
Console.ReadKey();
}
}
}
Reference
C# Unity register a list of registered types and resolve by adding N instances
Registering Types and Object Instances