gpt4 book ai didi

xamarin.ios - ViewModels 和 Service 类的实例化

转载 作者:行者123 更新时间:2023-12-04 02:30:27 24 4
gpt4 key购买 nike

我试图理解 ViewModels 和 Service 类的实例化并写下来供其他人使用。请在需要的地方更正/添加。

ViewModel 和服务的实例化不是以最常见的方式完成的。它是使用反射完成的。

在 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 结尾)的接口(interface)和类使用反射以及接口(interface)名称和类名称(IPersonService 和 PersonService)配对。这稍后用于反向查找类的实例(查找表包含对服务类的单例实例的惰性引用。服务在 null 时创建。

public FirstViewModel(ICalculationServicecalculationService) 引用CalculationService 的一个实例。这是通过使用先前创建的查找表来完成的。

ViewModels 的实例化是通过 Mvx 框架完成的。当 MvxFramework 被“询问”实例化的 ViewModel 时,它将反射(reflect) ViewModel 并确定该类上有哪些构造函数。如果有一个无参数的构造函数,那么它将被使用。如果有一个带参数的构造函数,并且该参数是服务类的接口(interface),则该服务的(单例)实例将用作参数。

服务以类似的方式实例化;它们的构造函数被反射并且参数被实例化(单例)。等等。

最佳答案

这里使用的想法是:

  • 服务定位器模式
  • 控制反转

  • 有很多关于此的文章和介绍 - 一些好的起点是 Martin Fowler's introductionJoel Abrahamsson's IoC introduction 。我也做了一些 animated slides 作为简单的演示。

    特别是在 MvvmCross 中,我们提供了一个静态类 Mvx,它充当注册和解析接口(interface)及其实现的单一位置。
    服务地点 - 注册和解决
    MvvmCross Service Location 的核心思想是您可以编写如下类和接口(interface):

    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 将被创建并返回
  • 所有后续调用都将获得第一次创建的相同实例

  • “动态”注册
    最后一个选择是,您可以将 IFooFoo 对注册为:
    // every time someone needs an IFoo they will get a new one
    Mvx.RegisterType<IFoo, Foo>();
    在这种情况下,每次调用 Mvx.Resolve<IFoo>() 都会创建一个新的 Foo - 每次调用都会返回不同的 Foo
    最后注册的胜利
    如果您创建一个接口(interface)的多个实现并将它们全部注册:
    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();
    此代码使用反射来:
  • 查找核心程序集中的所有类
  • creatable - 即:
  • 有一个公共(public)构造函数
  • 不是 abstract

  • 名称以服务
  • 结尾

  • 找到它们的接口(interface)
  • 根据它们支持的接口(interface)将它们注册为惰性单例

  • 技术说明: 这里的懒惰单例实现是相当技术性的——它确保如果一个类实现了 IOneITwo,那么当解析 23123123131313141 和 IOne 时,将返回相同的 2031231231231231313131313141 实例。
    选择以这里结尾的名称 - ITwo - 以及选择使用 Lazy 单例只是个人习惯。如果您更喜欢为您的对象使用其他名称或其他生命周期,您可以使用不同的调用或多个调用替换此代码,例如:
    CreatableTypes()
    .EndingWith("SingleFeed")
    .AsInterfaces()
    .RegisterAsLazySingleton();
    CreatableTypes()
    .EndingWith("Generator")
    .AsInterfaces()
    .RegisterAsDynamic();
    CreatableTypes()
    .EndingWith("QuickSand")
    .AsInterfaces()
    .RegisterAsSingleton();
    如果您愿意,您还可以在那里使用其他 Service 辅助方法来帮助进一步定义您的注册 - 例如 LinqInheritsExcept , WithAttribute , Containing ... 例如
            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>();
    选择是 你的
    构造函数注入(inject)
    除了 InNamespace 之外, Mvx.Resolve<T> 静态类还提供了一种基于反射的机制,可以在对象构造期间自动解析参数。
    例如,如果我们添加一个类,如:
    public class Bar
    {
    public Bar(IFoo foo)
    {
    // do stuff
    }
    }
    然后您可以使用以下方法创建此对象:
        Mvx.IocConstruct<Bar>();
    在这次通话中发生的事情是:
  • MvvmCross:
  • 使用反射找到 Mvx
  • 的构造函数
  • 查看该构造函数的参数,发现它需要一个 Bar
  • 使用 IFoo 来获取 Mvx.Resolve<IFoo>()
  • 的注册实现
  • 使用反射调用带有 IFoo 参数的构造函数


  • 构造函数注入(inject)和 ViewModel
    创建 ViewModel 时,这种“构造函数注入(inject)”机制在 MvvmCross 内部使用。
    如果你声明一个 ViewModel 像:
     public class MyViewModel : MvxViewModel
    {
    public MyViewModel(IMvxJsonConverter jsonConverter, IMvxGeoLocationWatcher locationWatcher)
    {
    // ....
    }
    }
    那么当创建 IFoo 时,MvvmCross 将使用 Mvx 静态类来解析 jsonConverterlocationWatcher 的对象。
    这很重要 因为:
  • 它可以让你轻松地提供不同的平台上(在iPhone上,你可以使用一个观察家认为谈话MyViewModel,在Windows Phone,你可以使用一个观察家认为会谈locationWatcher
  • 不同 CoreLocation
  • 它允许您在单元测试中轻松提供模拟实现
  • 它允许您覆盖默认实现 - 如果您不喜欢 Json 的 System.Device.Location 实现,您可以改用 Json.Net 实现。

  • 构造函数注入(inject)和链接
    在内部, ServiceStack.Text 机制在需要新对象时使用构造函数注入(inject)。
    这使您能够注册依赖于其他接口(interface)的实现,例如:
    public interface ITaxCalculator
    {
    double TaxDueFor(int customerId)
    }

    public class TaxCalculator
    {
    public TaxCalculator(ICustomerRepository customerRepository, IForeignExchange foreignExchange, ITaxRuleList taxRuleList)
    {
    // code...
    }

    // code...
    }
    如果您随后将此计算器注册为:
    Mvx.RegisterType<ITaxCalculator, TaxCalculator>();
    然后,当客户端调用 Mvx.Resolve<T> 时,MvvmCross 将创建一个新的 Mvx.Resolve<ITaxCalculator>() 实例,解析所有 TaxCalculator 、 0x251812231343151 和 10413 操作期间的所有 0x251812231343151 和 1813 操作。
    此外,这个过程是 递归 - 所以如果这些返回的对象中的任何一个需要另一个对象 - 例如如果您的 ICustomerRepository 实现需要一个 IForeignExchange 对象 - 那么 MvvmCross 也会为您提供 Resolve 。
    当我在不同平台上需要不同的实现时,如何使用 IoC?
    有时您需要在 ViewModel 中使用某些特定于平台的功能。例如,您可能希望在 ViewModel 中获取当前屏幕尺寸 - 但没有现有的可移植 .Net 调用来执行此操作。
    当您想要包含这样的功能时,有两个主要选择:
  • 在您的核心库中声明一个接口(interface),然后在您的每个 UI 项目中提供并注册一个实现。
  • 使用或创建插件

  • 1. 具有平台特定实现的 PCL 接口(interface)
    在您的核心项目中,您可以声明一个接口(interface),并且您可以在您的类中使用该接口(interface) - 例如:
    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 项目中,您可以为 ITaxRuleList 声明特定于平台的实现。一个简单的例子是:
    public class WindowsPhoneScreenSize : IScreenSize
    {
    public double Height { get { return 800.0; } }
    public double Width { get { return 480.0; } }
    }
    然后,您可以在每个特定于平台的安装文件中注册这些实现 - 例如你可以用 IForeignExchange 覆盖
    protected override void InitializeFirstChance()
    {
    Mvx.RegisterSingleton<IScreenSize>(new WindowsPhoneScreenSize());
    base.InitializeFirstChance();
    }
    完成此操作后,将在每个平台上为 IChargeCommission 提供正确的平台特定实现 IScreenSize
    2. 使用或创建插件
    插件是一种 MvvmCross 模式,用于组合 PCL 程序集,以及可选的一些特定于平台的程序集,以便打包一些功能。
    这个插件层只是一个模式——一些简单的约定——用于命名相关的程序集,包括小的 MvxSetup.InitializeFirstChanceMyViewModel 辅助类,以及使用 IoC。通过这种模式,它允许跨平台和跨应用程序轻松地包含、重用和测试功能。
    例如,现有插件包括:
  • 文件插件,提供对 IScreenSize 类型方法的访问,用于操作文件
  • 位置插件,提供对地理位置信息的访问
  • Messenger 插件,提供对 Messenger/事件聚合器的访问
  • PictureChooser 插件,提供对相机和媒体库的访问
  • ResourceLoader 插件,它提供了一种访问打包在应用程序
  • 的 .apk、.app 或 .ipa 中的资源文件的方法
  • 一个 SQLite 插件,可在所有平台上访问 PluginLoader

  • 插件使用
    如果您想了解如何在您的应用程序中使用这些插件,那么:
  • N+1 个视频提供了一个很好的起点 - 参见 http://mvvmcross.wordpress.com/ - 特别是:
  • N=8 - 位置 http://slodge.blogspot.co.uk/2013/05/n8-location-location-location-n1-days.html
  • N=9 - 信使 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


  • 插件创作
    编写插件很容易,但一开始可能会觉得有点令人生畏。
    关键步骤是:
  • 为插件创建主 PCL 程序集 - 这应该包括:
  • 你的插件将注册的接口(interface)
  • 任何共享的可移植代码(可能包括一个或多个接口(interface)的实现)
  • 一个特殊的 Plugin 类,MvvmCross 将使用它来启动插件

  • 可选择创建特定于平台的程序集,其中:
  • 与主程序集的名称相同,但具有特定于平台的扩展名(.Droid、.WindowsPhone 等)
  • 包含
  • 任何特定于平台的接口(interface)实现
  • 一个特殊的 System.IO 类,MvvmCross 将使用它来启动这个特定于平台的扩展


  • 可选择提供额外的文件,如文档和 NuGet 打包,这将使插件更易于重用。

  • 我不会在这里详细介绍编写插件。
    如果您想了解有关编写自己的插件的更多信息,请执行以下操作:
  • https://speakerdeck.com/cirrious/plugins-in-mvvmcross
  • 上有一个介绍
  • 有一个示例,它在 https://github.com/slodge/MvvmCross-Tutorials/tree/master/GoodVibrations
  • 创建了一个 SQLite-net 插件

    如果...
    如果...我不想使用 Service Location 或 IoC 怎么办
    如果你不想在你的代码中使用它,那就不要。
    只需从 App.cs 中删除 PluginLoader 代码,然后在您的 ViewModel 中使用“普通代码” - 例如:
    public class MyViewModel : MvxViewModel
    {
    private readonly ITaxService _taxService;

    public MyViewModel()
    {
    _taxService = new TaxService();
    }
    }
    如果...我想使用不同的服务位置或 IoC 机制怎么办
    有很多 优秀的 库,包括 AutoFac、Funq、MEF、OpenNetCF、TinyIoC 等等!
    如果要替换 MvvmCross 实现,则需要:
  • 写某种 Plugin 层来提供他们的服务位置代码作为 Vibrate
  • 覆盖 0x25181231343141 类中的 CreatableTypes()... 以提供此替代 Adapter 实现。

  • 或者,您可以组织一种混合情况——其中两个 IoC/ServiceLocation 系统并排存在。
    如果...我想使用属性注入(inject)作为 IoC 机制怎么办
    提供了一个用于 IoC 的示例属性注入(inject)实现。
    这可以使用设置覆盖初始化:
    protected override IMvxIoCProvider CreateIocProvider()
    {
    return MvxPropertyInjectingIoCContainer.Initialise();
    }
    如果...我想要高级 IoC 功能,例如子容器怎么办
    MvvmCross 中的 IoC 容器设计得非常轻量级,并且针对我构建的移动应用程序所需的功能级别。
    如果您需要更高级/复杂的功能,那么您可能需要使用不同的提供程序或不同的方法 - 对此的一些建议在: Child containers in MvvmCross IoC 中讨论

    关于xamarin.ios - ViewModels 和 Service 类的实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16967454/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com