Prism研究(for WPF & Silverlight)7.View Injection和View Discovery

  一切从这里开始。Prism 文档对于这两个概念的解释着实有些扯淡,甚至自相抵牾,还是不要看了,乖乖听额讲吧。

    我们晓得,每个Module中,实现了IModule接口的Module类,都要实现该接口的Initialize方法,一方面要注册一些自定义的接口和实现了该接口的类,为接下来的依赖注入做准备;另一方面,就是为Region加载View并显示(初始化View)。

    加载View的方式,分为View InjectionView Discovery两类,各有千秋。

1.       View Injection,指的是RegionManager,它的属性Regions,这是一个集合类RegionCollection,同时还实现了基于RegionName的内部索引。于是,我们可以这样编写代码:

    View-first:

this.regionManager.Regions["MainRegion"].Add(new HelloWorldView());

    Presenter-first:

this.regionManager.Regions["MainRegion"].Add(this.container.Resolve<HelloWorldPresenter>().View);

    有关View-firstPresenter-first的概念,我们在下一节中专门介绍。

    View Injection的视图如下,有点像中介者模式吧,那个Registry就是一个Mediator


   
    注意,
View Injection只适用于RegionManager,不适用于RegionViewRegistry,原因么,看我下面的分析。

2.       View Discovery,指的是RegionViewRegistryRegisterViewWithRegion方法,有2个重载:

        public void RegisterViewWithRegion(string regionName, Type viewType)

        {

            this.RegisterViewWithRegion(regionName, () => this.CreateInstance(viewType));

        }

 

        public void RegisterViewWithRegion(string regionName, Func<object> getContentDelegate)

        {

            this.registeredContent.Add(regionName, getContentDelegate);

            this.OnContentRegistered(new ViewRegisteredEventArgs(regionName, getContentDelegate));

        }

 

    我们看到,第1个方法是基于第2个方法的,就是把RegionName和一个delegate(用于创建View的一个实例)同时注册到字典ListDictionary<string, Func<object>>和委托链WeakDelegatesManager中。

    于是我们可以这样编写代码:

    View-first:

            this. RegionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));

    Presenter-first:

           this. RegionViewRegistry.RegisterViewWithRegion("MainRegion",

() => this.container.Resolve<IHelloWorldPresenter>().View);

 

    有关View-firstPresenter-first的概念,我们在下一节中专门介绍。

    View Discovery的视图如下,看这个图让我想起了GOF中的注册工厂:



   
Prism文档中P18说到,View Discovery适用于V-first,而View Injection同时适用于V-firstP-first。这是不恰当的。看了我上面的分析和代码,就会发现View Discovery也有以P-first方式实现的,而且也是很常用的。

    View InjectionView Discovery,本来就是2个不同类——RegionManagerRegionViewRegistry的不同实现方式,连存储结构都不同,前者是集合,后者是字典。相对而言,前者的实现更直接一些(来一个招呼一个),后者比较绕(先对号入座,然后再用到谁招呼谁)。

    存储结构的不同,使得二者在性能上有很大差异。RegionManagerRegions属性是一个集合,所以可以手动添加或移除Region中的View(使用集合的AddRemove方法),尤其是移除功能,可以释放不再使用的内存。

    相对而言,RegionViewRegistry在内部使用字典来存储View,它的RegisterViewWithRegion方法就是把View添加到这个字典中,但是,由于这个字典是私有的,不对外暴露,所以我们无法使用它的Remove方法来移除View。这是因为,我们存储到字典中的View都是弱引用,再回顾一下:

            this. RegionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));

    这里typeof(Views.HelloWorldView)就是一个弱引用,我们知道,.NET会自动回收不再使用的内存,所以,理想情况下,不再使用这个View时,该View所占的内存就会被自动回收了。但是,自动回收内存在什么时候发生呢?没人知道。所以,系统越来越慢,就是因为RegionViewRegistry的这个方法导致的。

    既然不好用,那为什么还要设计出RegionViewRegistry呢?

    说起来话长了。这要从Prism的第1版说起。话说,那时Prism中只有View Injection机制,也就是说,只有RegionManager类。后来,大家发现RegionManager类也有不好的地方,就是在嵌套Region的时候,每一级Region中都要写点Regions[“RegionName”].Add方法,而且还要care层次间的关系,是不是觉得很麻烦?而且Prism经常会报错说找不到RegionName,以致于抛出异常(关于这一点,请参见我的另一篇文章)。为了方便开发,p&p小组在Prism2版中提供了RegionViewRegistry类,及其RegisterViewWithRegion方法,于是我们可以在任何地方使用该方法,而不需要在各自的View中编写相应的代码。但是,简单是要付出代价的,那就是性能,上文已经分析过。

    同时,Prism框架把RegisterViewWithRegion也作为扩展方法添加到了RegionManager类中,实际上还是调用RegionViewRegistry类的RegisterViewWithRegion方法。于是,形成了一个组合关系。

    本来设计思想是蛮好的,可以屏蔽RegionViewRegistry类,对Client而言,只有RegionManager类,它同时具有RegisterViewWithRegion方法和Regions[“RegionName”]属性(前者是弱引用方式,后者是强引用方式)。

    但是,不知是微软p & p的那个DPE大脑进水,在Prism文档和示例又同时使用了RegionViewRegistryRegisterViewWithRegion方法。这就违背了设计的初衷。于是,我们糊涂了,不知道什么时候该使用哪一个类的哪一个方法,甚至开始怀疑人生。

相关文章
相关标签/搜索