輕量級MVVM框架Stylet介紹:(14) StyletIoC


簡介

StyletIoC輕量級的極快速的IoC容器。

服務和構建

StyletIoC是圍繞服務的概念構建的。服務是具體類型、抽象類型或接口,由具體類型實現,例如:

interface IVehicle { ... }
class HotHatchback : IVehicle { ... }

這里IVehicle是服務,HotHatchback是實現它的具體類型。請注意,HotHatchback也是一個服務 - 由類本身實現的服務。

配置 StyletIoC 時,需要定義一組關系。每個關系都介於服務與實現它的類型(或多個類型)之間。所以在這里,我們可以告訴StyletIoC"在服務IVehicle和類型HotHatchback之間建立一種關系"。

稍后,當你想要實現IVehicle時,你可以要求StyletIoC"給我一個實現IVehicle服務的實例,StyletIoC將構造一個HotHatchback並將其傳回給你。

解析類型 - 服務定位器和注入

有3種方法可以讓StyletIoC為我們構建一個類型:

  • 通過直接調用IContainer.Get
  • 構造函數注入
  • 屬性注入

直接調用IContainer.Get,如下例:

var ioc = ... // Covered in lots of detail elsewhere
var vehicle = ioc.Get<IVehicle>();

看起來很簡單,但是缺點是只能在應用程序的根目錄才能這樣做,在其他地方還是需要使用構造函數和參數注入。

構造函數注入的例子

class Engine { ... }
class Car
{
   public Car(Engine engine)
   {
      // 'engine' contains a new instance of Engine
   }
}

屬性注入的例子:

class Engine { ... }
class Car
{
   [Inject]
   public Engine Engine { get; set; }
}

StyletIoC Configuration

入門 - 構建器

使用StyletIoC容器的方法是首先調用StyletIoCBuilder構建容器,然后在其上注冊服務。例如:

// First, create the builder
var builder = new StyletIoCBuilder();

// Configure the builder
builder.Bind<IVehicle>().To<HotHatchback>();

// The container is then built for you

// Now you can use this to resolve instances of services
var vehicle = ioc.Get<IVehicle>();

創建類型的不同方式

類型綁定

代碼如下:
builder.Bind<IVehicle>().To<HotHatchback>();
這表示告知StyletIoC:每當要求提供一個IVehicle的實現,就使用適當的構造函數創建一個HotHatchback的新實例,並將所必要的依賴傳入。

也可以將服務綁定到自身,這是一種類型約束,稱之為“自綁定”,例如:

builder.Bind<HotHatchback>().To<HotHatchback>();
// Or, more clearly:
builder.Bind<HotHatchback>().ToSelf();

這表示告知StyletIoC:每當要求提供一個HotHatchback的實現,就給出一個早已包含所有依賴的HotHatchback。

也可以使用非范型版本:
builder.Bind(typeof(IVehicle)).To(typeof(HotHatchback));

工廠綁定

可以告知StyletIoC如何構建類型,傳入一個委托,稱之為“工廠緘定”,例如:

builder.Bind<IVehicle>().ToFactory(container => new HotHatchback());

這里的container參數是IContainer類型,可以用來注入構造函數中必要的依賴,例如:

builder.Bind<IVehicle>().ToFactory(container => new HotHatchback(container.Get<Engine>()));

當然,這里也支持自綁定:

builder.Bind<HotHatchback>().ToFactory(container => new HotHatchback());

實例綁定

也可以將一個自己創建的實例注入容器,例如:
builder.Bind<IVehicle>().ToInstance(new HotHatchback());
實例綁定原生是單例的,因為容器不知道如何構建該類型的新實例,因此始終返回相同的實例。

作用范圍

Transient

默認情況下,StyletIoC每次都會創建一個新的實例,稱之Transient:

var car1 = ioc.Get<IVehicle>();
var car2 = ioc.Get<IVehicle>();

// The following statement will succeed.
Assert.AreNotEqual(car1, car2);

IoC容器不會主動釋放Transient實例,如果需要,請手動處理。

單例

你可以告知IoC僅創建唯一一個服務的實例,這有利於單元測試:

builder.Bind<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();
var ioc = builder.BuildContainer();

var configManager1 = ioc.Get<IConfigurationManager>();
var configManager2 = ioc.Get<IConfigurationManager();

// The following statement will succeed.
Assert.AreEqual(configManager1, configManager2);

當然也可以使用工廠綁定方式構建單例:
builder.Bind<IConfigurationManager>().ToFactory(container => new ConfigurationManager()).InSingletonScope();

采用不同方式構建的單例並不是同一對象:

builder.Bind<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();
builder.Bind<ConfigurationManager>().ToSelf();

var ioc = builder.BuildContainer();

var configManager1 = ioc.Get<IConfigurationManager>();
var configManager2 = ioc.Get<ConfigurationManager>();

// The following statement will succeed.
Assert.AreNotEqual(configManager1, configManager2);

如果你想要讓其返回同一實例,可以這樣做:

builder.Bind<ConfigurationManager>().And<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();

將多個類型綁定到單一服務

例如:

interface IVehicle { ... }
class HotHatchback : IVehicle { ... }
class OldBanger : IVehicle { ... }

builder.Bind<IVehicle>().To<HotHatchback>();
builder.Bind<IVehicle>().To<OldBanger>();

var ioc = builder.BuildContainer();

// This will throw an exception
ioc.Get<IVehicle>();

// This will return { new HotHatchback(), new OldBanger() }
IEnumerable<IVehicle> vehicles = ioc.GetAll<IVehicle>();

如果需要獲取集合,應該使用IContainer.GetAll方法,使用IContainer.Get,因為無法判斷是哪一個,會拋出異常。

這個規則同樣適用於構造函數及參數注入:
class Garage
{
public Garage(IEnumerable vehicles) { ... }
}

// And

class Garage
{
[Inject]
public IEnumerable Vehicles { get; set; }
}

綁定范型

范型綁定的方法與上面類似:

interface IValidator<T> { ... }
class IntValidator : IValidator<int> { ... }
builder.Bind<IValidator<int>>().To<IntValidator>();
interface IValidator<T> { ... }
class Validator<T> : IValidator<T> { ... }
builder.Bind<IValidator<int>>().To<Validator<int>>();

有趣的是,可以為未知的泛型類型創建綁定:

interface IValidator<T> { ... }
class Validator<T> : IValidator<T> { ... }
builder.Bind(typeof(IValidator<>)).To(typeof(Validator<>));

var ioc = builder.BuildContainer();

var intValidator = ioc.Get<IValidator<int>>(); // Returns a Validator<int>

服務及其實現都可以有任意數量的類型參數,但它們必須有相同數量的類型參數(如果你仔細考慮的話,這是有道理的)。 但是,類型參數可以按任何順序出現:

interface ISomeInterface<T, U> { ... }
class SomeClass<U, T> : ISomeInterface<T, U> { ... }
builder.Bind(typeof(ISomeInterface<,>)).To(typeof(SomeClass<,>));

StyletIoC 不考慮類型約束——如果你有一個接口 IMyInterface where T: class 並請求一個 IMyInterface ,你會得到一個異常。

自動綁定

自動綁定所有實體類型

自動綁定意味着如果您請求一個尚未向 Stylet 注冊的具體類型,Stylet 將嘗試為您構建它作為臨時實例。 它僅適用於您指定的程序集中的類型:StyletIoCBuilder.Assemblies 中的類型,以及您傳遞給 Autobind 方法的任何程序集中的類型。

請注意,顯式綁定始終優先於自動綁定。

將諸如 mscorlib 之類的程序集傳遞給 Autobind 是一個壞主意,否則 Stylet 將嘗試實例化諸如 System.String 之類的類型。

builder.Autobind();
這在 MVVM 應用程序中很有用,因為它允許 StyletIoC 解析您的任何 ViewModel。 Stylet 引導程序調用 Autobind(),這意味着默認情況下啟用自動綁定。

綁定一個服務到所有實現

interface IVehicle { ... }
class HotHatchback : IVehicle { ... }
class OldBanger : IVehicle { ... }

builder.Bind<IVehicle>().ToAllImplementations();

var ioc = builder.BuildContainer();

IEnumerable<IVehicle> vehicles = ioc.GetAll<IVehicle>(); // Returns { new HotHatchback(), new OldBanger() }

還有一些重載允許您指定要搜索的程序集。

這本身就很有用(想想查找所有插件),但在與未綁定的泛型結合使用時特別有用。 例如:

interface IValidator<T> { ... }
class IntValidator : IValidator<int> { ... }
class StringValidator : IValidator<string> { ... }

builder.Bind(typeof(IValidator<>)).ToAllImplementations();

var ioc = builder.BuildContainer();

var intValidator = ioc.Get<IValidator<int>>(); // Returns an IntValidator
var stringValidator = ioc.Get<IValidator<string>>(); // Returns a StringValidator

如果你想要更復雜的綁定規則,StyletIoC 並沒有為你提供 API——你自己做幾乎不需要任何努力,而提供 API 只是增加了很多復雜性而收效甚微。

但是,StyletIoC 確實在 Type 上定義了一些擴展方法,這可能會讓您的生活更輕松:

// Returns all base types
someType.GetBaseTypes();

// Returns all base types and interfaces
someType.GetBaseTypesAndInterfaces();

// Returns true if someType implements someServiceType
someType.Implements(someServiceType)
// Also takes into account generics - so this is true:
typeof(Validator<int>>.Implements(typeof(IValidator<>));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM