我试图了解ViewModels和Service类的实例化,并将其写下来供其他人使用。请在需要的地方更正/加载。
ViewModels和Services的实例化不是以最常见的方式完成的。它是用反射完成的。
在TipCalc中你有:
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()期间,设计为Service的接口和类(名称以Service结尾)使用反射以及接口名称和类名称(IPersonService和PersonService)进行配对。这稍后用于反向查找类的实例(查找表包含对服务类的单例实例的延迟引用。服务在null时创建。
public FirstViewModel(ICalculationService calculationService)引用CalculationService的实例。这是通过使用先前创建的查找表来完成的。
ViewModels的实例化是通过Mvx框架完成的。当“询问”MvxFramework实例化的ViewModel时,它将反映ViewModel并确定该类上的构造函数。如果有一个无参数构造函数,那么将使用它。如果有一个带参数的构造函数且参数是服务类的接口,那么该服务的(单例)实例将用作参数。
服务以类似的方式实例化;他们的构造函数反映和参数实例化(单例)。等等。
答案 0 :(得分:62)
这里使用的想法是:
有很多关于此的文章和介绍 - 一些好的起点是Martin Fowler's introduction和Joel Abrahamsson's IoC introduction。我还做了一些animated slides作为一个简单的演示。
特别是在MvvmCross中,我们提供了一个静态类Mvx
,它作为注册和解析接口及其实现的单一位置。
MvvmCross服务位置的核心思想是您可以编写类和接口,如:
public interface IFoo
{
string Request();
}
public class Foo : IFoo
{
public string Request()
{
return "Hello World";
}
}
编写完这一对后,您可以将Foo
实例注册为使用以下实现IFoo
的单例:
// every time someone needs an IFoo they will get the same one
Mvx.RegisterSingleton<IFoo>(new Foo());
如果您这样做,那么任何代码都可以调用:
var foo = Mvx.Resolve<IFoo>();
每次调用都会返回Foo的相同实例。
作为对此的修改,您可以注册一个懒惰的单身人士。这是写的
// 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
最后一个选项是,您可以将IFoo
和Foo
对注册为:
// every time someone needs an IFoo they will get a new one
Mvx.RegisterType<IFoo, Foo>();
在这种情况下,每次拨打Mvx.Resolve<IFoo>()
都会创建一个新的Foo
- 每次通话都会返回不同的Foo
。
如果您创建了几个接口实现并将它们全部注册:
Mvx.RegisterType<IFoo, Foo1>();
Mvx.RegisterSingleton<IFoo>(new Foo2());
Mvx.RegisterType<IFoo, Foo3>();
然后每次通话取代之前的注册 - 所以当客户端拨打Mvx.Resolve<IFoo>()
时,将返回最近的注册。
这对以下内容非常有用:
IUserInfo
实现替换为真实实现。 MvvmCross的默认NuGet模板包含核心App.cs
中的代码块,如:
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
此代码使用Reflection来:
creatable
- 即:
abstract
技术说明:这里的懒惰单例实现非常技术性 - 它确保如果一个类实现IOne
和ITwo
,那么在解析两者时将返回相同的实例IOne
和ITwo
。
在此结束的名称选择 - Service
- 以及使用懒惰单身人士的选择只是个人约定。如果您希望为对象使用其他名称或其他生命周期,则可以使用其他调用或多次调用替换此代码,例如:
CreatableTypes()
.EndingWith("SingleFeed")
.AsInterfaces()
.RegisterAsLazySingleton();
CreatableTypes()
.EndingWith("Generator")
.AsInterfaces()
.RegisterAsDynamic();
CreatableTypes()
.EndingWith("QuickSand")
.AsInterfaces()
.RegisterAsSingleton();
如果您愿意,还可以使用其他Linq
辅助方法来帮助您进一步定义注册 - 例如Inherits
,Except
。 WithAttribute
,Containing
,InNamespace
......例如
CreatableTypes()
.StartingWith("JDI")
.InNamespace("MyApp.Core.HyperSpace")
.WithAttribute(typeof(MySpecialAttribute))
.AsInterfaces()
.RegisterAsSingleton();
当然,您也可以在除Core之外的其他程序集上使用相同类型的注册逻辑 - 例如:
typeof(Reusable.Helpers.MyHelper).Assembly.CreatableTypes()
.EndingWith("Helper")
.AsInterfaces()
.RegisterAsDynamic();
或者,如果您不想使用这种基于反射的注册,那么您只需手动注册您的实现:
Mvx.RegisterSingleton<IMixer>(new MyMixer());
Mvx.RegisterSingleton<ICheese>(new MyCheese());
Mvx.RegisterType<IBeer, Beer>();
Mvx.RegisterType<IWine, Wine>();
选择你的。
与Mvx.Resolve<T>
一样,Mvx
静态类提供了一种基于反射的机制,可在对象构造过程中自动解析参数。
例如,如果我们添加一个类:
public class Bar
{
public Bar(IFoo foo)
{
// do stuff
}
}
然后您可以使用以下方法创建此对象:
Mvx.IocConstruct<Bar>();
在此次通话中会发生什么:
Bar
IFoo
Mvx.Resolve<IFoo>()
来获取IFoo
IFoo
参数这&#34;构造函数注入&#34;在创建ViewModel时,在MvvmCross内部使用机制。
如果您声明ViewModel,请执行以下操作:
public class MyViewModel : MvxViewModel
{
public MyViewModel(IMvxJsonConverter jsonConverter, IMvxGeoLocationWatcher locationWatcher)
{
// ....
}
}
然后,当创建Mvx
时,MvvmCross将使用jsonConverter
静态类来解析locationWatcher
和MyViewModel
的对象。
这很重要因为:
locationWatcher
课程(在iPhone上您可以使用与CoreLocation
对话的观察者,在Windows Phone上,您可以使用与{{1}对话的观察者}} System.Device.Location
实现,则可以使用Json.Net
实现。在内部,ServiceStack.Text
机制在需要新对象时使用构造函数注入。
这使您可以注册依赖于其他接口的实现,如:
Mvx.Resolve<T>
如果您将此计算器注册为:
public interface ITaxCalculator
{
double TaxDueFor(int customerId)
}
public class TaxCalculator
{
public TaxCalculator(ICustomerRepository customerRepository, IForeignExchange foreignExchange, ITaxRuleList taxRuleList)
{
// code...
}
// code...
}
然后,当客户端调用Mvx.RegisterType<ITaxCalculator, TaxCalculator>();
时,MvvmCross将创建一个新的Mvx.Resolve<ITaxCalculator>()
实例,在操作期间解析所有TaxCalculator
,ICustomerRepository
和IForeignExchange
。
此外,此过程递归 - 因此,如果这些返回的对象中的任何一个需要另一个对象 - 例如如果您的ITaxRuleList
实施需要IForeignExchange
对象 - 那么MvvmCross也将为您提供Resolve。
有时您需要在ViewModel中使用某些特定于平台的功能。例如,您可能希望在ViewModel中获取当前屏幕尺寸 - 但是没有现成的便携式.Net调用。
当您想要包含这样的功能时,有两个主要选择:
在您的核心项目中,您可以声明一个接口,并且可以在您的类中使用该接口 - 例如:
IChargeCommission
在每个UI项目中,您可以为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); }
}
}
声明特定于平台的实现。一个简单的例子是:
IScreenSize
然后,您可以在每个特定于平台的安装文件中注册这些实现 - 例如您可以使用
覆盖public class WindowsPhoneScreenSize : IScreenSize
{
public double Height { get { return 800.0; } }
public double Width { get { return 480.0; } }
}
MvxSetup.InitializeFirstChance
完成此操作后,protected override void InitializeFirstChance()
{
Mvx.RegisterSingleton<IScreenSize>(new WindowsPhoneScreenSize());
base.InitializeFirstChance();
}
将在每个平台上为MyViewModel
提供正确的特定于平台的实现。
插件是一个MvvmCross模式,用于组合PCL程序集,以及可选的某些特定于平台的程序集,以便打包一些功能。
这个插件层只是一个模式 - 一些简单的约定 - 用于命名相关的程序集,包括小IScreenSize
和PluginLoader
辅助类,以及使用IoC。通过这种模式,它允许跨平台和跨应用程序轻松地包含,重用和测试功能。
例如,现有插件包括:
Plugin
类型操作文件的方法的访问System.IO
。如果您想了解如何在您的应用程序中使用这些插件,那么:
编写插件很容易,但最初会感到有点令人生畏。
关键步骤是:
为插件创建主PCL程序集 - 这应包括:
SQLite-net
类可选择创建特定于平台的程序集:
PluginLoader
类可选择提供文档和NuGet打包等附加功能,使插件更易于重复使用。
我不打算在此处详细介绍插件。
如果您想了解更多关于编写自己的插件的信息,请:
Plugin
插件的示例
如果您不想在代码中使用此功能,请不要这样做。
只需从App.cs中删除Vibrate
代码,然后使用&#39;普通代码&#39;在你的ViewModels中 - 例如:
CreatableTypes()...
有很多优秀的库,包括AutoFac,Funq,MEF,OpenNetCF,TinyIoC以及许多其他库!
如果您想要替换MvvmCross实现,那么您需要:
public class MyViewModel : MvxViewModel
{
private readonly ITaxService _taxService;
public MyViewModel()
{
_taxService = new TaxService();
}
}
图层,以便将其服务位置代码作为Adapter
IMvxIoCProvider
课程中的CreateIocProvider
,以提供此替代Setup
实施。或者,您可以组织混合情况 - 两个IoC / ServiceLocation系统并排存在。
IoC提供了一个示例Property Injection实现。
可以使用以下设置覆盖来初始化:
IMvxIoCProvider
MvvmCross中的IoC容器设计得非常轻巧,并且针对我构建的移动应用程序所需的功能级别。
如果您需要更高级/复杂的功能,那么您可能需要使用不同的提供商或不同的方法 - 有关此问题的一些建议将在以下讨论:Child containers in MvvmCross IoC