65.9K
CodeProject 正在变化。 阅读更多。
Home

Xamarin.Forms 的更智能的 DI 容器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (2投票s)

2018年12月26日

CPOL

3分钟阅读

viewsIcon

7749

他们在使用 IOC 容器时犯了错误,但这是可以修复的。博文《Xamarin.Forms 的更智能的 DI 容器》首次出现在 Marcus Technical Services 上。

终于,一个没有虚假“IOC”幌子的 DI 容器

这只是依赖注入——仅此而已

现代程序员需要一种从作为参数传递的其他类创建新类的方法,并且以优雅、无缝的方式进行。这些新实例通常需要缓存,尤其是在它们是服务或视图模型的情况下。解决此挑战的方案一直是错误命名和严重错误描述的“IOC 容器”。在另一篇文章中,我解释了为什么这是一个坏主意。如果我们甚至无法准确地命名某些东西,那么是时候完全重新考虑它了。因此,我在这里使用我们所有人都实际需要的工具完成了这项工作:DI (依赖注入)容器。

它有什么特别智能的地方

1. 它提供真正的生命周期管理

IOC 容器总是在您创建实例时存储它。这是非常浪费的。它也是不安全的Smart DI Container仅提供三种类型的注册

  • 任何访问级别:使用此选项可以随时创建实例,而无需缓存。在Resolve()上检索的局部变量是唯一存储的引用。
  • 实例之间共享:当您使用此访问级别Resolve()时,您必须传入一个“parent”对象,该对象被索引到该新类实例。您还可以通过再次调用Resolve<>将同一个实例链接到任意数量的其他使用者/父对象。一旦所有父对象都消失,缓存的实例也会被删除。注意:这需要您引发一个Xamarin.Forms事件来通知容器关于共享实例父对象的死亡。如果您还使用我们的Lifecycle Aware Library,我们将为您完成此操作。
  • 全局单例:容器创建并缓存以该访问级别注册的任何类型的永久实例。当容器本身超出范围时,缓存的引用将消失。

尽管市场上其他容器的尺寸庞大,但它们都无法通过此测试。容器必须提供一个物理机制,使此功能成为可能。我们有一个!

2. 它不是全局或静态的

您可以随处声明 Smart DI Container 的实例。这支持“嵌套”场景,其中容器存在于狭义定义的类继承树中。记住:所有存储/缓存的“global”变量仅在容器存在时有效。

3. 它行为良好

Smart DI Container 会防止递归调用或任何其他违反您所做的基于规则的注册的行为。例如,如果您为同一基本类型注册两个相互竞争的接口

_container = new SmartDIContainer();
_container.RegisterTypeAsInterface<FirstSimpleClass>(typeof(IAmSimple));
_container.RegisterTypeAsInterface<SecondSimpleClass>(typeof(IAmSimple));

... 然后解析IAmSimple,则创建了一个冲突。容器无法知道返回哪一个。您可以设置一个布尔属性在这种情况下抛出错误。或者您可以提供一个“冲突解析器”

var simple = _container.Resolve<IAmSimple>
(StorageRules.AnyAccessLevel, null, ForbidSpecificClass<FirstSimpleClass>);

private static IConflictResolution ForbidSpecificClass<T>
(IDictionary<Type, ITimeStampedCreatorAndStorageRules> registrations)
{
   // Find any registration where the key 
   // (the main class that was registered and that is being constructed) is *not* the forbidden one
   var legalValues = registrations.Where(r => r.Key != typeof(T)).ToArray();

   if (legalValues.IsEmpty())
   {
      return null;
   }

   return new ConflictResolution
                {
                   MasterType                = legalValues.First().Key,
                   TypeToCastWithStorageRule = legalValues.First().Value.CreatorsAndStorageRules.First()
                };
      }

4. 它非常小

Smart DI Container 几乎不占用任何空间,并且很少触及内存,因为它不会不必要地存储任何内容。

5. 它是基本的、诚实的开源 C#,并且易于阅读

我们实际上添加了注释!(我们没有被闪电击中。)

6. 它经过测试和验证

请参阅单元测试

快速入门

在需要的地方创建 DI 容器;不要集中

DI 容器既注册变量,又提供对变量的访问。为了符合 C# SOLID 指南,您的应用程序应尽可能私有。因此,您最不需要的就是全局容器。也许服务可以以这种方式存储。但不是您的视图模型。考虑使用一个工厂来生成视图模型,并规范哪些存储在容器中,哪些只是完全实例化

public interface IViewModel<t> {}

public interface IViewModelFactory
{
   IViewModel CreateSimpleViewModel<T>();
   IViewModel CreateGlobalViewModel<T>();
   IViewModel CreateSharedViewModel<T>(object o);
}

public class ViewModelFactory : IViewModelFactory
{
   private SmartDIContainer _viewModelContainer = new SmartDIContainer();

   public ViewModelFactory(IService service)
   {
      // Perform registrations at the constructor, privately.

      // Can pass in services to seed this container or can Resolve this factory from another, 
      // more global container
      _smartDIContainer.RegisterTypeAsInterface<SomeService>
             (typeof(IService), StorageRules.AnyAccessLevel, o => service);

      // Register other known types using various access levels
      _smartDIContainer.RegisterTypeAsInterface<SomeClass>(typeof(ISomeClass));
      _smartDIContainer.RegisterTypeAsInterface<GlobalClass>(typeof(IGlobalClass), 
                                                        StorageRules.GlobalSingleton);
      _smartDIContainer.RegisterTypeAsInterface<SharedClass>(typeof(ISharedClass), 
                                             StorageRules.SharedDependencyBetweenInstances);
   }

   public IViewModel CreateSimpleViewModel<T>()
   {
      return _container.Resolve<T>();
   }

   public IViewModel CreateGlobalViewModel()
   {
      return _container.Resolve<T>(StorageRules.GlobalSingleton);
   }

   public IViewModel CreateSharedViewModel(object o)
   {
      return _container.Resolve<T>(StorageRules.SharedDependencyBetweenInstances, o);
   }
}

实施 LifecycleAware 指南

您可以轻松地使您的Xamarin.Forms页面、视图和视图模型符合生命周期管理。请参阅描述和代码示例

如果您已经有自己的基类,您可以将LifecycleAware库中的基类示例中的一些代码片段复制到这些现有文件中。

如果这太麻烦,您仍然可以使用 Smart DI Container 的“Any”和“Global”访问级别,这涵盖了较小应用程序的大多数情况。

博文Xamarin.Forms 的更智能的 DI 容器首次出现在 Marcus Technical Services 上。

© . All rights reserved.