问题描述
我试图理解ViewModels和Service类的实例化,并为其他人写下来.请在需要的地方更正/添加.
I tried to understand the instantiation of ViewModels and Service classes and wrote it down for others. Please correct/add-on where needed.
ViewModels和Services的实例化不是以最常见的方式完成的.这是通过反射完成的.
The instantiation of ViewModels and Services is not done in the most-common way. It's done using reflection.
在TipCalc中,您可以:
In the TipCalc you have:
public class FirstViewModel : MvxViewModel
{
private readonly ICalculationService _calculationService;
public FirstViewModel(ICalculationService calculationService)
{
_calculationService = calculationService;
}
...
}
和
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
public override void Initialize()
{
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
...
}
}
在Initialize()期间,使用反射和接口名称和类名称(IPersonService和PersonService)配对设计为服务的接口和类(名称以Service结尾).稍后将其用于对类的实例进行反向查找(查找表包含对服务类的单例实例的惰性引用.服务在null时创建.
During Initialize() Interfaces and classes designed as Service (name ends with Service) are paired up using reflection and the interface names and class names (IPersonService and PersonService). This is later used to reverse lookup the instance of the class (the lookup table contains lazy references to singleton instances of the service classes. Services are created when null.
公共FirstViewModel(ICalculationService CalculationService)引用CalculationService的实例.这是通过使用先前创建的查找表完成的.
public FirstViewModel(ICalculationService calculationService) references an instance of CalculationService. This is done by using the lookup table earlier created.
ViewModels的实例化是通过Mvx框架完成的.当为实例化的ViewModel询问" MvxFramework时,它将反映ViewModel并确定该类上有哪些构造函数.如果有一个无参数的构造函数,则将使用该构造函数.如果有一个带参数的构造函数,并且该参数是服务类的接口,则该服务的(单个)实例将用作参数.
Instantiation of ViewModels is done through the Mvx framework. When the MvxFramework is 'asked' for an instantiated ViewModel, it will reflect the ViewModel and determine what constructors there are on that class. If there's a parameterless constructor, then that will be used. If there is a constructor with a parameter and the parameter is the interface of a service class, then the (singleton) instance of that service will be used as the parameter.
以类似的方式实例化服务.他们的构造函数反映出来,实例化参数(单例).依此类推.
Services are instantiated in a similar way; their constructors reflected and parameters instantiated (singleton). And so on.
推荐答案
此处使用的想法是:
- 服务定位器模式
- 控制反转
关于此的文章和介绍很多-一些很好的起点是马丁·福勒的介绍和 Joel Abrahamsson的IoC简介 .我还制作了一些动画幻灯片示范.
There are lots of articles and introductions available on this - some good starting places are Martin Fowler's introduction and Joel Abrahamsson's IoC introduction. I've also made some animated slides as a simple demonstration.
特别是在MvvmCross中,我们提供了一个静态类Mvx
,该类充当注册和解析接口及其实现的单个位置.
Specifically within MvvmCross, we provide a single static class Mvx
which acts as a single place for both registering and resolving interfaces and their implementations.
MvvmCross服务位置的核心思想是您可以编写类和接口,如:
The core idea of MvvmCross Service Location is that you can write classes and interfaces like:
public interface IFoo
{
string Request();
}
public class Foo : IFoo
{
public string Request()
{
return "Hello World";
}
}
单一注册
写完这对后,您可以将Foo
实例注册为使用以下方法实现IFoo
的单例:
Singleton Registration
With this pair written you could then register a Foo
instance as a singleton which implements IFoo
using:
// every time someone needs an IFoo they will get the same one
Mvx.RegisterSingleton<IFoo>(new Foo());
如果您这样做了,那么任何代码都可以调用:
If you did this, then any code can call:
var foo = Mvx.Resolve<IFoo>();
,并且每次调用都会返回 Foo的相同实例.
and every single call would return the same instance of Foo.
为此,您可以注册一个懒惰的单身人士.这是写的
As a variation on this, you could register a lazy singleton. This is written
// every time someone needs an IFoo they will get the same one
// but we don't create it until someone asks for it
Mvx.RegisterSingleton<IFoo>(() => new Foo());
在这种情况下:
- 最初没有创建
Foo
- 任何代码第一次调用
Mvx.Resolve<IFoo>()
时,都会创建并返回一个新的Foo
- 所有后续调用都将获得与第一次创建的实例相同的
- no
Foo
is created initially - the first time any code calls
Mvx.Resolve<IFoo>()
then a newFoo
will be created and returned - all subsequent calls will get the same instance that was created the first time
最后一个选择是,您可以将IFoo
和Foo
对注册为:
One final option, is that you can register the IFoo
and Foo
pair as:
// every time someone needs an IFoo they will get a new one
Mvx.RegisterType<IFoo, Foo>();
在这种情况下,每次调用Mvx.Resolve<IFoo>()
都会创建一个新的Foo
-每次调用都会返回不同的Foo
.
In this case, every call to Mvx.Resolve<IFoo>()
will create a new Foo
- every call will return a different Foo
.
如果您创建了接口的几种实现并全部注册了它们:
If you create several implementations of an interface and register them all:
Mvx.RegisterType<IFoo, Foo1>();
Mvx.RegisterSingleton<IFoo>(new Foo2());
Mvx.RegisterType<IFoo, Foo3>();
然后每个呼叫替换先前的注册-因此,当客户呼叫Mvx.Resolve<IFoo>()
时,将返回最新的注册.
Then each call replaces the previous registration - so when a client calls Mvx.Resolve<IFoo>()
then the most recent registration will be returned.
这对以下情况可能有用:
This can be useful for:
- 覆盖默认实现
- 根据应用程序状态替换实现-例如在对用户进行身份验证之后,您可以用一个真实的实现替换一个空的
IUserInfo
实现.
- overwriting default implementations
- replacing implementations depending on application state - e.g. after a user has been authenticated then you could replace an empty
IUserInfo
implementation with a real one.
MvvmCross的默认NuGet模板在核心App.cs
中包含一个代码块,例如:
The default NuGet templates for MvvmCross contain a block of code in the core App.cs
like:
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
此代码使用反射来:
- 在Core程序集中查找所有类
- 是
creatable
-即:- 有一个公共构造函数
- 不是
abstract
- find all classes in the Core assembly
- which are
creatable
- i.e.:- have a public constructor
- are not
abstract
技术说明:此处的懒惰单例实现是非常技术性的-它确保了如果类实现
IOne
和ITwo
,则在解析IOne
和IOne
时将返回相同的实例.ITwo
.Technical Note: the lazy singleton implementation here is quite technical - it ensures that if a class implements
IOne
andITwo
then the same instance will be returned when resolving bothIOne
andITwo
.以此处结尾的名称-
Service
-和使用Lazy单例的选择只是个人习惯.如果您希望为对象使用其他名称或其他生存期,则可以将此代码替换为另一个调用或多个调用,例如:The choice of name ending here -
Service
- and the choice to use Lazy singletons are only personal conventions. If you prefer to use other names or other lifetimes for your objects you can replace this code with a different call or with multiple calls like:CreatableTypes() .EndingWith("SingleFeed") .AsInterfaces() .RegisterAsLazySingleton(); CreatableTypes() .EndingWith("Generator") .AsInterfaces() .RegisterAsDynamic(); CreatableTypes() .EndingWith("QuickSand") .AsInterfaces() .RegisterAsSingleton();
如果需要,您还可以使用其他
Linq
帮助器方法来帮助进一步定义您的注册-例如Inherits
,Except
.WithAttribute
,Containing
,InNamespace
...例如There you can also use additional
Linq
helper methods to help further define your registrations if you want to - e.g.Inherits
,Except
.WithAttribute
,Containing
,InNamespace
... e.g.CreatableTypes() .StartingWith("JDI") .InNamespace("MyApp.Core.HyperSpace") .WithAttribute(typeof(MySpecialAttribute)) .AsInterfaces() .RegisterAsSingleton();
当然,您还可以在Core以外的程序集上使用相同类型的注册逻辑-例如:
And you can also, of course, use the same type of registration logic on assemblies other than Core - e.g.:
typeof(Reusable.Helpers.MyHelper).Assembly.CreatableTypes() .EndingWith("Helper") .AsInterfaces() .RegisterAsDynamic();
或者,如果您不想使用这种基于反射的注册,则可以手动注册实现:
Alternatively, if you prefer not to use this Reflection based registration, then you can instead just manually register your implementations:
Mvx.RegisterSingleton<IMixer>(new MyMixer()); Mvx.RegisterSingleton<ICheese>(new MyCheese()); Mvx.RegisterType<IBeer, Beer>(); Mvx.RegisterType<IWine, Wine>();
选择为您的.
和
Mvx.Resolve<T>
一样,Mvx
静态类提供了一种基于反射的机制,可以在对象构建期间自动解析参数.As well as
Mvx.Resolve<T>
, theMvx
static class provides a reflection-based mechanism to automatically resolve parameters during object construction.例如,如果我们添加类似的类:
For example, if we add a class like:
public class Bar { public Bar(IFoo foo) { // do stuff } }
然后您可以使用以下对象创建该对象:
Then you can create this object using:
Mvx.IocConstruct<Bar>();
在此通话中发生的是:
- MvvmCross:
- 使用Reflection查找
Bar
的构造函数 - 查看该构造函数的参数,发现它需要一个
IFoo
- 使用
Mvx.Resolve<IFoo>()
来获取IFoo
的已注册实现. - 使用反射使用
IFoo
参数调用构造函数
- MvvmCross:
- uses Reflection to find the constructor of
Bar
- looks at the parameters for that constructor and sees it needs an
IFoo
- uses
Mvx.Resolve<IFoo>()
to get hold of the registered implementation forIFoo
- uses Reflection to call the constructor with the
IFoo
parameter
在创建ViewModel时,在MvvmCross内部使用此构造函数注入"机制.
This "Constructor Injection" mechanism is used internally within MvvmCross when creating ViewModels.
如果您声明类似的ViewModel:
If you declare a ViewModel like:
public class MyViewModel : MvxViewModel { public MyViewModel(IMvxJsonConverter jsonConverter, IMvxGeoLocationWatcher locationWatcher) { // .... } }
然后,当创建
MyViewModel
时,MvvmCross将使用Mvx
静态类来解析jsonConverter
和locationWatcher
的对象.then MvvmCross will use the
Mvx
static class to resolve objects forjsonConverter
andlocationWatcher
when aMyViewModel
is created.这很重要,因为:
- 它使您可以轻松地在不同平台上提供不同的
locationWatcher
类(在iPhone上,您可以使用与CoreLocation
对话的观察者;在Windows Phone上,您可以使用与System.Device.Location
对话的观察者 - 它使您可以轻松地在单元测试中提供模拟实现
- 它允许您覆盖默认实现-如果您不喜欢Json的
Json.Net
实现,则可以改用ServiceStack.Text
实现.
- It allows you to easily provide different
locationWatcher
classes on different platforms (on iPhone you can use a watcher that talk toCoreLocation
, on Windows Phone you can use a watcher that talks toSystem.Device.Location
- It allows you to easily provide mock implementations in your unit tests
- It allows you to override default implementations - if you don't like the
Json.Net
implementation for Json, you can use aServiceStack.Text
implementation instead.
构造函数注入和链接
在内部,
Mvx.Resolve<T>
机制在需要新对象时使用构造函数注入.Constructor Injection and Chaining
Internally, the
Mvx.Resolve<T>
mechanism uses constructor injection when new objects are needed.这使您可以注册依赖于其他接口的实现,例如:
This enables you to register implementations which depend on other interfaces like:
public interface ITaxCalculator { double TaxDueFor(int customerId) } public class TaxCalculator { public TaxCalculator(ICustomerRepository customerRepository, IForeignExchange foreignExchange, ITaxRuleList taxRuleList) { // code... } // code... }
如果您随后将此计算器注册为:
If you then register this calculator as:
Mvx.RegisterType<ITaxCalculator, TaxCalculator>();
然后,当客户端调用
Mvx.Resolve<ITaxCalculator>()
时,MvvmCross将创建一个新的TaxCalculator
实例,在操作过程中解析所有ICustomerRepository
,IForeignExchange
和ITaxRuleList
.Then when a client calls
Mvx.Resolve<ITaxCalculator>()
then MvvmCross will create a newTaxCalculator
instance, resolving all ofICustomerRepository
,IForeignExchange
andITaxRuleList
during the operation.此外,此过程是递归-因此,如果这些返回的对象中的任何一个都需要另一个对象,例如如果您的
IForeignExchange
实现需要一个IChargeCommission
对象-那么MvvmCross也会为您提供Resolve.Further, this process is recursive - so if any of these returned objects requires another object - e.g. if your
IForeignExchange
implementation requires aIChargeCommission
object - then MvvmCross will provide Resolve for you as well.有时,您需要在ViewModels中使用某些特定于平台的功能.例如,您可能想在ViewModel中获取当前的屏幕尺寸-但目前尚无可移植的.Net调用.
Sometimes you need to use some platform specific functionality in your ViewModels. For example, you might want to get the current screen dimensions in your ViewModel - but there's no existing portable .Net call to do this.
如果要包括这样的功能,则有两个主要选择:
When you want to include functionality like this, then there are two main choices:
- 在核心库中声明一个接口,然后在每个UI项目中提供并注册一个实现.
- 使用或创建插件
1.具有平台特定实现的PCL接口
在核心项目中,您可以声明一个接口,然后可以在其中的类中使用该接口-例如:
1. PCL-Interface with Platform-Specific Implementation
In your core project, you can declare an interface and you can use that interface in your classes there - e.g.:
public interface IScreenSize { double Height { get; } double Width { get; } } public class MyViewModel : MvxViewModel { private readonly IScreenSize _screenSize; public MyViewModel(IScreenSize screenSize) { _screenSize = screenSize; } public double Ratio { get { return (_screenSize.Width / _screenSize.Height); } } }
然后在每个UI项目中,可以声明
IScreenSize
的特定于平台的实现.一个简单的例子是:In each UI project, you can then declare the platform-specific implementation for
IScreenSize
. A trivial example is:public class WindowsPhoneScreenSize : IScreenSize { public double Height { get { return 800.0; } } public double Width { get { return 480.0; } } }
然后您可以在每个特定于平台的安装文件中注册这些实现-例如您可以使用覆盖
MvxSetup.InitializeFirstChance
You can then register these implementations in each of the platform-specific Setup files - e.g. you could override
MvxSetup.InitializeFirstChance
withprotected override void InitializeFirstChance() { Mvx.RegisterSingleton<IScreenSize>(new WindowsPhoneScreenSize()); base.InitializeFirstChance(); }
完成此操作后,
MyViewModel
将在每个平台上为IScreenSize
提供正确的平台特定实现.With this done, then
MyViewModel
will get provided with the correct platform-specific implementation ofIScreenSize
on each platform.插件是一种MvvmCross模式,用于组合PCL程序集以及可选的一些特定于平台的程序集,以打包某些功能.
A Plugin is an MvvmCross pattern for combining a PCL assembly, plus optionally some platform-specific assemblies in order to package up some functionality.
此插件层只是一个模式-一些简单的约定-用于命名相关的程序集,包括小的
PluginLoader
和Plugin
助手类,以及用于IoC.通过这种模式,可以轻松地跨平台和跨应用程序包含,重用和测试功能.This plugin layer is simply a pattern - some simple conventions - for naming related Assemblies, for including small
PluginLoader
andPlugin
helper classes, and for using IoC. Through this pattern it allows functionality to be easily included, reused and tested across platforms and across applications.例如,现有的插件包括:
For example, existing plugins include:
- 文件插件,可访问用于处理文件的
System.IO
类型方法 - 一个位置插件,可访问GeoLocation信息
- 一个Messenger插件,可以访问Messenger/Event Aggregator
- 一个PictureChooser插件,可以访问相机和媒体库
- ResourceLoader插件,它提供了一种方法来访问应用程序的.apk,.app或.ipa中打包的资源文件
- 一个SQLite插件,可在所有平台上访问
SQLite-net
.
- a File plugin which provides access to
System.IO
type methods for manipulating files - a Location plugin which provides access to GeoLocation information
- a Messenger plugin which provides access to a Messenger/Event Aggregator
- a PictureChooser plugin which provides access to the camera and to the media library
- a ResourceLoader plugin which provides a way to access resource files packaged within the .apk, .app or .ipa for the application
- a SQLite plugin which provides access to
SQLite-net
on all platforms.
如果您想了解如何在您的应用程序中使用这些插件,则:
If you want to see how these plugins can be used in your applications, then:
- N + 1个视频提供了一个很好的起点-请参见 http://mvvmcross.wordpress.com/-特别是:
- N = 8-位置 http ://slodge.blogspot.co.uk/2013/05/n8-location-location-location-n1-days.html
- N = 9-Messenger http://slodge.blogspot.co.uk/2013/05/n9-getting-message-n1-days-of-mvvmcross.html
- N = 10-SQLite http ://slodge.blogspot.co.uk/2013/05/n10-sqlite-persistent-data-storage-n1.html
- N = 12-> N = 17-Collect-A-Bull应用程序 http://slodge.blogspot.co.uk/2013/05/n12-collect-bull-full-app-part-1-n1.html
- the N+1 videos provide a good starting point - see http://mvvmcross.wordpress.com/ - especially :
- N=8 - Location http://slodge.blogspot.co.uk/2013/05/n8-location-location-location-n1-days.html
- N=9 - Messenger http://slodge.blogspot.co.uk/2013/05/n9-getting-message-n1-days-of-mvvmcross.html
- N=10 - SQLite http://slodge.blogspot.co.uk/2013/05/n10-sqlite-persistent-data-storage-n1.html
- N=12 -> N=17 - the Collect-A-Bull app http://slodge.blogspot.co.uk/2013/05/n12-collect-bull-full-app-part-1-n1.html
编写插件很容易,但是起初可能会有些令人生畏.
Writing plugins is easy to do, but can feel a bit daunting at first.
关键步骤是:
-
为插件创建主PCL程序集-其中应包括:
Create the main PCL Assembly for the plugin - this should include:
- 您的插件将注册的接口
- 任何共享的可移植代码(可能包括一个或多个接口的实现)
- MvvmCross将用来启动插件的特殊
PluginLoader
类
(可选)创建特定于平台的程序集,
Optionally create platform-specific assemblies which:
- 被命名为与主装配体相同,但具有特定于平台的扩展名(.Droid,.WindowsPhone等)
- 包含
- 任何特定于平台的界面实现
- MvvmCross将用来启动此特定于平台的扩展的特殊
Plugin
类
- are named the same as the main assembly but with a platform specific extension (.Droid, .WindowsPhone, etc.)
- contains
- any platform-specific interface implementations
- a special
Plugin
class which MvvmCross will use to start this platform-specific extension
(可选)提供诸如文档和NuGet打包之类的附加功能,这将使插件更易于重用.
Optionally provide extras like documentation and NuGet packaging which will make the plugin easier to reuse.
在这里,我将不进一步详细介绍如何编写插件.
I'm not going to go into any more detail on writing plugins here.
如果您想了解有关编写自己的插件的更多信息,那么:
If you'd like to see more about writing your own plugin, then:
- 在 https://speakerdeck.com/cirrious/plugins-in- mvvmcross
- 有一个示例在 https://github上创建
Vibrate
插件.com/slodge/MvvmCross-Tutorials/tree/master/GoodVibrations
- there's a presentation on this at https://speakerdeck.com/cirrious/plugins-in-mvvmcross
- there's a sample which creates a
Vibrate
plugin at https://github.com/slodge/MvvmCross-Tutorials/tree/master/GoodVibrations
如果您不想在代码中使用它,那就不要.
If you don't want to use this in your code, then don't.
只需从App.cs中删除
CreatableTypes()...
代码,然后在您的ViewModels中使用普通代码"-例如:Simply remove the
CreatableTypes()...
code from App.cs and then use 'normal code' in your ViewModels - e.g.:public class MyViewModel : MvxViewModel { private readonly ITaxService _taxService; public MyViewModel() { _taxService = new TaxService(); } }
如果...我想使用其他服务位置或IoC机制怎么办
有很多优秀库,包括AutoFac,Funq,MEF,OpenNetCF,TinyIoC等等!
What if... I want to use a different Service Location or IoC mechanism
There are lots of excellent libraries out there including AutoFac, Funq, MEF, OpenNetCF, TinyIoC and many, many more!
如果要替换MvvmCross实现,则需要:
If you want to replace the MvvmCross implementation, then you'll need to:
- 编写某种
Adapter
层以提供其服务位置代码作为IMvxIoCProvider
在 - 覆盖
CreateIocProvider
以提供此替代IMvxIoCProvider
实现.
Setup
类中- write some kind of
Adapter
layer to provide their service location code as anIMvxIoCProvider
- override
CreateIocProvider
in yourSetup
class to provide this alternativeIMvxIoCProvider
implementation.
或者,您可以组织混合情况-两个IoC/ServiceLocation系统并排存在.
Alternatively, you may be able to organise a hybrid situation - where two IoC/ServiceLocation systems exist side-by-side.
提供了一个用于IoC的属性注入实现示例.
There is an example Property Injection implementation for IoC provided.
这可以使用以下设置的覆盖进行初始化:
This can be initialised using a Setup override of:
protected override IMvxIoCProvider CreateIocProvider() { return MvxPropertyInjectingIoCContainer.Initialise(); }
如果...我想要子容器等高级IoC功能怎么办
MvvmCross中的IoC容器设计得非常轻巧,并且针对我构建的移动应用程序所需的功能级别.
What if... I want advanced IoC features like child containers
The IoC container in MvvmCross is designed to be quite lightweight and is targeted at a level of functionality required in the mobile applications I have built.
如果您需要更多高级/复杂功能,则可能需要使用其他提供程序或其他方法-有关此问题的一些建议,请参见:
If you need more advanced/complex functionality, then you may need to use a different provider or a different approach - some suggestions for this are discussed in: Child containers in MvvmCross IoC
这篇关于ViewModels和Service类的实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- uses Reflection to find the constructor of
- 使用Reflection查找
- which are
- 是