03-Bind绑定
1.什么是绑定
每一种依赖注入的框架其实都只是将一种类型绑定到实例的框架。 在Zenject中,绑定就是向容器注册给定的类型,然后容器通过递归解析给定对象的所有依赖关系,并创建对象的实例。要告诉容器如何解析这些依赖项,就要通过Bind命令来实现。
public class StartInstaller : MonoInstaller
{public override void InstallBindings(){Container.Bind().AsSingle();}
}public class HelloWorldService
{public void HelloWorld(){Debug.Log("Hello World!");}
}
在具体讲解Bind命令之前,要先了解什么是容器。
2.DiContainer容器
在没有使用依赖注入框架之前,我们通过手工管理依赖对象。一般是在类中主动创建依赖对象,从而导致类与类之间高度耦合。在使用了依赖注入框架之后,创建和查找依赖对象的工作交给了IOC容器,由容器统一创建并注入对象,因而对象之间是松散耦合,便于维护。这就是容器的作用。
之前的代码中进行绑定操作的Container实际上是Zenject通过注入方式,获取到的一个DiContainer的实例。
我们也可以自己创建一个DiContainer的实例
public class StartInstaller : MonoInstaller
{public override void InstallBindings(){var container = new DiContainer();container.Bind().AsSingle();// 解析HelloWorldService service = container.Resolve();service.HelloWorld();}
}public class HelloWorldService
{public void HelloWorld(){Debug.Log("Hello World!");}
}
甚至可以手动注入
public class HelloWorldExample : MonoBehaviour
{[Inject] private HelloWorldService _helloWorldService;private void Start(){var container = new DiContainer();container.Bind().AsSingle();// 注入container.Inject(this);_helloWorldService.HelloWorld();}
}
public class HelloWorldService
{public void HelloWorld(){Debug.Log("Hello World!");}
}
通过这种方式,我们就可以完全抛弃Zenject的Installer、Context的概念。
总结:
DiContainer即依赖注入容器,主要提供了如下三个API:
(1)注册(绑定)类型 Bind
(2)生成/获取类型的实例 Resolve
(3)注入类型实例到所需的脚本/类中 Inject
DiContainer的职责如下:
(1)管理注册的类型及类型的查询方式
(2)管理类型的实例
(3)管理类型的实例生成过程
3.Bind命令
下面我们正式开始介绍Bind命令。下面是Bind命令的完整格式,但一般情况下我们并不会使用这里面所有的方法。
ContractType与ResultType
ContractType
:约定类型,对应被注入的字段/参数的类型,一般是接口或抽象类
ResultType
:结果类型,要绑定到的类型,必须是ContractType的子类、实现类或与ContractType一样的类型。如果不显式声明将默认为ContractType一样的类型。
public class HelloWorldExample : MonoBehaviour
{private void Start(){var container = new DiContainer();container.Bind().To().AsSingle();IHelloWorldService service = container.Resolve();service.HelloWorld();}
}public interface IHelloWorldService
{public void HelloWorld();
}
public class HelloWorldService:IHelloWorldService
{public void HelloWorld(){Debug.Log("Hello World!");}
}
Scope作用域
用来确定生成的实例在多次注入中是否重用或重用的次数。
AsSingle()
:在Container中以单例形式存在。
AsTransient()
:在每次解析时都返回一个新的实例。如果没有显式声明则默认为此项。
AsCached()
:为每个约定类型创建一个实例。(没解析过的类型创建一个新的实例,解析过的类型返回创建过的实例)。
public class InstallerExample:MonoInstaller
{public override void InstallBindings(){Container.Bind().AsSingle();Container.Bind().AsTransient();Container.Bind().AsCached();}
}
Identifier
用于唯一标识绑定的值。在区分具有相同契约类型的多个绑定的情况下会用到。
Arguments
指定构造结果类型的实例时要用到的对象列表。
InstantiatedCallback
在某些情况下需要对实例化后的对象进行自定义修改,可以传递一个方法给OnInstantiated。
public class InstallerExample:MonoInstaller
{public override void InstallBindings(){Container.Bind().AsSingle().OnInstantiated((contex,obj)=>((ExampleA)obj).Name = "123");}
}
public class ExampleA
{public string Name { get; set; }
}
Condition
条件绑定,后面会讲到。
(Copy|Move)Into(All|Direct)SubContainers
用于让子容器自动继承绑定,很少会用到。
// 将唯一的Foo实例复制绑定到每个子容器
Container.Bind().AsSingle().CopyIntoAllSubContainers()
NonLazy
通常情况下,结果类型只有在绑定后首次使用时才会进行实例化(懒加载),通过显式声明NonLazy,可以让结果类在应用程序启动时就创建实例。
IfNotBound
如果已经存在一个相同的绑定(标识符也相同),则这次绑定会被跳过。
构造方法
FromNew
通过结果类型的构造方法创建,如果没有显式声明Bind的构造方法就会默认用此方法。
// 以下两种写法等同
Container.Bind();
Container.Bind().FromNew();
FromInstance
通过给定的实例绑定到容器,但给定的实例不会被注入。
Container.Bind().FromInstance(new Foo());
Container.BindInstance(new Foo());
Container.BindInstances(5.13f, "foo", new Foo());
FromMethod
通过自定义方法绑定,方法需要返回一个实例
Container.Bind().FromMethod(SomeMethod);Foo SomeMethod(InjectContext context)
{return new Foo();
}
也可以通过FromMethodMultiple一次性返回多个实例
Container.Bind().FromMethodMultiple(GetFoos);IEnumerable GetFoos(InjectContext context)
{return new Foo[]{new Foo(),new Foo(),new Foo(),}
}
FromFactory
通过自定义工厂类创建实例。因为工厂本身可以注入依赖关系,因此在逻辑或依赖比较复杂时推荐使用。
class FooFactory : IFactory
{public Foo Create(){return new Foo();}
}Container.Bind().FromFactory()
也可以通过FromIFactory为你的自定义工厂使用任何类型的构造方法
class FooFactory : ScriptableObject, IFactory
{public Foo Create(){return new Foo();}
}Container.Bind().FromIFactory(x => x.To().FromScriptableObjectResource("FooFactory")).AsSingle();
FromComponentInNewPrefab
从给定的预制体的实例上获取到结果类型的组件,然后绑定到容器。
Container.Bind().FromComponentInNewPrefab(somePrefab);
// 也可以直接通过资源路径获取预制体
Container.Bind().FromComponentInNewPrefabResource("Some/Path/Foo");
// 在与当前上下文以及所有父上下文相关的场景层次结构中查找指定的组件
Container.Bind().FromComponentInHierarchy().AsSingle();
// 从当前transform查找指定组件
Container.Bind().FromComponentSibling();
// 从当前transform及其父来查找组件
Container.Bind().FromComponentInParents();
// 从当前transform及其子来查找组件
Container.Bind().FromComponentInChildren();
FromNewComponentOnNewGameObject
创建一个空的游戏对象,然后在上面实例化一个结果类型的组件,并绑定到容器。
Container.Bind().FromNewComponentOnNewGameObject();
// 从给定预制体进行实例化
Container.Bind().FromNewComponentOnNewPrefab(somePrefab);
// 通过资源路径获取预制体
Container.Bind().FromNewComponentOnNewPrefabResource("Some/Path/Foo");
// 直接在给定的游戏物体上实例化组件
Container.Bind().FromNewComponentOn(someGameObject);
// 在当前transform上实例化组件
Container.Bind().FromNewComponentSibling();
// 从当前上下文的根来实例化给定组件
Container.Bind().FromNewComponentOnRoot();
FromResource
通过调用Unity中的Resources.Load
创建结果类型
Container.Bind().WithId("Glass").FromResource("Some/Path/Glass");
FromScriptableObjectResource
直接绑定给定资源路径下的ScriptableObject实例
public class Foo : ScriptableObject{}
Container.Bind().FromScriptableObjectResource("Some/Path/Foo");
// 如果希望ScriptableObject对象保存的值不受运行时更改的影响,应该使用如下方法,该方法会创建一个新的副本
Container.Bind().FromNewScriptableObjectResource("Some/Path/Foo");
FromResolve
通过容器解析DiContainer.Resolve
来获取实例。
public interface IFoo{}public interface IBar : IFoo{}public class Foo : IBar{}Container.Bind().To().FromResolve();
Container.Bind().To();
使用FromResolveGetter
从另一个依赖项的属性中获取实例
public class Bar
{
}public class Foo
{public Bar GetBar(){return new Bar();}
}Container.Bind();
Container.Bind().FromResolveGetter(x => x.GetBar());
使用FromSubContainerResolve
从子容器中获取
Container.Bind().FromSubContainerResolve().ByMethod(InstallFooFacade).WithKernel();void InstallFooFacade(DiContainer subContainer)
{subContainer.Bind();subContainer.Bind().To();
}
标签:
相关文章
-
无相关信息