使用LazyInject <>進行即時解析(Just-In-Time Resolving Using LazyInject<>)
在某些情況下需要在啟動后延遲創(chuàng)建某些依賴項,您可以使用LazyInject <>構造來實現(xiàn)。
比如,假設有這樣一個腳本:
public class Foo
{
public void Run()
{
...
}
}
public class Bar
{
Foo _foo;
public Bar(Foo foo)
{
_foo = foo;
}
public void Run()
{
_foo.Run();
}
}
public class TestInstaller : MonoInstaller<TestInstaller>
{
public override void InstallBindings()
{
Container.Bind<Foo>().AsSingle();
Container.Bind<Bar>().AsSingle();
}
}
假設我們只想在真正使用時才創(chuàng)建一個Foo的實例(也就是調(diào)用Bar.Run方法時)。 如上所述,即使從未調(diào)用過Bar.Run,每次創(chuàng)建Bar``時都會創(chuàng)建Foo。 我們可以通過將Bar```更改為以下內(nèi)容來解決此問題:
public class Bar
{
LazyInject<Foo> _foo;
public Bar(LazyInject<Foo> foo)
{
_foo = foo;
}
public void Run()
{
_foo.Value.Run();
}
}
現(xiàn)在,通過使用LazyInject <>,直到第一次調(diào)用Bar.Run時才會創(chuàng)建Foo類的實例,之后,將會使用同個實例
請注意,這兩種情況下安裝器保持不變。任何注入依賴項都可以通過使用LazyInject <>變?yōu)檠舆t。
非泛型綁定(Non Generic bindings)
在某些情況下,您可能不知道要在編譯時綁定的確切類型。 在這些情況下,您可以使用Bind方法的重載,該方法采用System.Type值而不是泛型參數(shù)。
\\這兩行結果形同
Container.Bind(typeof(Foo));
Container.Bind<Foo>();
注意,使用非泛型綁定時,可以傳遞多個參數(shù):
Container.Bind(typeof(Foo), typeof(Bar), typeof(Qux)).AsSingle();
// 上面的一行等同于下面的三行:
Container.Bind<Foo>().AsSingle();
Container.Bind<Bar>().AsSingle();
Container.Bind<Qux>().AsSingle();
對于To方法同樣適用:
Container.Bind<IFoo>().To(typeof(Foo), typeof(Bar)).AsSingle();
// 上面的一行等同于下面的兩行:
Container.Bind<IFoo>().To<Foo>().AsSingle();
Container.Bind<IFoo>().To<Bar>().AsSingle();
或者,兩者混合:
Container.Bind(typeof(IFoo), typeof(IBar)).To(typeof(Foo1), typeof(Foo2)).AsSingle();
// 上面一行等同于下面幾行:
Container.Bind<IFoo>().To<Foo>().AsSingle();
Container.Bind<IFoo>().To<Bar>().AsSingle();
Container.Bind<IBar>().To<Foo>().AsSingle();
Container.Bind<IBar>().To<Bar>().AsSingle();
當您有一個類實現(xiàn)多個接口時,這可能特別有用:
Container.Bind(typeof(ITickable), typeof(IInitializable), typeof(IDisposable)).To<Foo>().AsSingle();
雖然在這個特定的例子中已經(jīng)存在了內(nèi)置的快捷方法:
Container.BindInterfacesTo<Foo>().AsSingle();
基于約定的綁定(Convention Based Binding)
基于約定的綁定可以在以下任何一種情況下派上用場:
- 您想定義一個命名約定,該約定確定類如何綁定到容器(例如,使用前綴,后綴或正則表達式)
- 您希望使用自定義屬性來確定類與容器的綁定方式
- 您希望自動綁定在給定命名空間或程序集中實現(xiàn)給定接口的所有類
使用“convention over configuration”可以允許您定義一個框架,其他程序員可以使用該框架快速輕松地完成任務,而不必在安裝器中明確添加每個綁定。 這是Ruby on Rails,ASP.NET MVC等框架所遵循的理念。當然,這種方法既有優(yōu)點也有缺點。
除了不用向Bind()和To()方法提供類型列表外,它們以與非泛型綁定類似的方式指定,您可以使用Fluent API描述約定。 例如,要將IFoo綁定到在整個代碼庫中實現(xiàn)它的每個類:
Container.Bind<IFoo>().To(x => x.AllTypes().DerivingFrom<IFoo>());
注意,您也可以在Bind()方法中使用相同的Fluent API,您也可以在Bind()和To()中同時使用它。
有關更多示例,請參閱下面的示例部分。 完整格式如下:
x.InitialList().Conditional().AssemblySources()
這里:
-
InitialList = 用于綁定的初始類型列表。 此列表將由給定的條件進行過濾。 它可以是以下(相當自我解釋)方法之一:
i.AllTypes
ii.AllNonAbstractClasses
iii.AllAbstractClasses
iv.AllInterfaces
v.AllClasses -
Conditional = 要應用于InitialList給出的類型列表的過濾器。 請注意,您可以根據(jù)需要將所有這些鏈接在一起,并且它們將按順序應用于初始列表。 它可以是以下之一:
i.DerivingFrom - 只匹配派生自T的類型
ii.DerivingFromOrEqual - 只匹配派生自T類型或T類型
iii.WithPrefix(value) - 只匹配名字以value開始的類型
iv.WithSuffix(value) - 只匹配名字以value結尾的類型
v.WithAttribute - 只匹配類以[T]特性修飾的類型
vi.WithoutAttribute - 只匹配類沒有以[T]特性修飾的類型
vii.WithAttributeWhere(predicate) - 僅匹配在類聲明之上具有屬性[T]的類型,并且在傳遞屬性時給定謂詞返回true。 這很有用,因此您可以使用賦予該屬性的數(shù)據(jù)來創(chuàng)建綁定
viii.InNamespace(value)-只匹配在給定命名空間中的類型
ix.InNamespaces(value1,value2,等)-只匹配在給定命名空間中的類型
x.MatchingRegex(pattern) - 只匹配符合給定正則表達式的類型
xi.Where(predicate) - 最后,您還可以通過傳入帶有Type參數(shù)的謂詞來添加任何類型的條件邏輯 -
AssemblySources = 填充InitialList時要搜索類型的程序集列表。 它可以是以下之一:
i.FromAllAssemblies - 在所有加載的程序集中查找類型,未指定時默認該值
ii.FromAssemblyContaining - 在包含[T]的程序集中查找類型
iii.FromAssembliesContaining(type1, type2, ..) - 在包含[T]的程序集中查找類型
iv.FromThisAssembly- 僅在要調(diào)用此方法的程序集中查找類型
v.FromAssembly(assembly) - 僅在給定的程序集中查找類型
vi.FromAssemblies(assembly1, assembly2, ...) - 僅在給定的程序集中查找類型
vii.FromAssembliesWhere(predicate) - 在與給定謂詞匹配的所有程序集中查找類型
例子:
請注意,您可以將以下項任意組合在同一個綁定中。 另請注意,由于我們未在此處指定程序集,因此Zenject將在所有已加載的程序集中進行搜索。
1.將IFoo綁定到整個代碼庫中實現(xiàn)它的每個類:
Container.Bind<IFoo>().To(x => x.AllTypes().DerivingFrom<IFoo>());
注意,以下結果相同:
Container.Bind<IFoo>().To(x => x.AllNonAbstractTypes());
這是因為Zenject將跳過具體類型不是從基類型派生的任何綁定。 另請注意,在這種情況下,我們必須確保使用AllNonAbstractTypes而不僅僅是AllTypes,以確保我們不將IFoo綁定到自身
2.將接口綁定到在給定命名空間內(nèi)實現(xiàn)它的所有類
Container.Bind<IFoo>().To(x => x.AllTypes().DerivingFrom<IFoo>().InNamespace("MyGame.Foos"));
3.自動綁定IController每個具有后綴“Controller”的類(如ASP.NET MVC中所做的那樣):
Container.Bind<IController>().To(x => x.AllNonAbstractTypes().WithSuffix("Controller"));
你也可以使用MatchingRegex:
Container.Bind<IController>().To(x => x.AllNonAbstractTypes().MatchingRegex("Controller$"));
4.使用前綴“Widget”綁定所有類型并注入Foo
Container.Bind<object>().To(x => x.AllNonAbstractTypes().WithPrefix("Widget")).WhenInjectedInto<Foo>();
5.自動綁定給定命名空間中每種類型使用的接口
Container.Bind(x => x.AllInterfaces())
.To(x => x.AllNonAbstractClasses().InNamespace("MyGame.Things"));
這相當于為名稱空間“MyGame.Things”中的每個類型調(diào)用Container.BindInterfacesTo <T>()。 這是有效的,因為如上所述,Zenject將跳過任何綁定,其中具體類型實際上不是從基類型派生的。 因此,即使我們使用AllInterfaces匹配每個加載的程序集中的每個單獨的接口,這也沒關系,因為它不會嘗試將接口綁定到不實現(xiàn)此接口的類型。
裝飾器綁定(Decorator Bindings)
見后續(xù)章節(jié)