• Component,Container,IServceContainer,ISite 使用初探


            大家好,大约半年多没在园子上发贴子了,主要是跳槽到新公司后一直在开发新产品,现在就要发布正式版了,
    是一个论坛软件,官方站点是http://nt.discuz.net
            不多说了,还是言归正传,前一阵子在网上看System.ComponentModel名空间下的一些类,主要是Component,
    Container,IServceContainer,ISite,一时兴起想好好看看这几个类的关系和如何使用,但因为MSDN上可用的资料不是
    很充足,而在BAINDU,GOOGLE上搜的基本上也只是一些只言片语,只有几个网页写的不错(基本上是老外),
    但也是偏重于讲底层的逻辑关系或从Injection方面讲的,而实例又相对复杂或与我感兴趣的方向无太大关系,当读
    上瘾之后文章就嘎然而止。这的确很郁闷,没办法,只有去用reflector 去反向.NET框架中的代码了,这才有了些领悟,
    也就是今天文章中要说的内容。这篇文章旨在抛砖引玉, 园子里的朋友肯定有精通此方面内容的,如果看到这篇文章,
    欢迎留下只言片语也好。

           上面提到的几个类的关系就不多说了,套用网上的话吧:
           容器包含组件,并且允许所包含的组件相互访问。当容器管理组件时,该容器负责在自身被处置时处置该组件。
           组件的生存期可以由它的容器来控制。作为生存期管理的回报,组件获得了对容器所提供的服务的访问权。此
    关系类似于 COM+ 组件与承载它的 COM+ 容器之间的关系。通过允许 COM+ 容器对其进行管理,COM+ 组件可
    以参与事务以及使用由 COM+ 容器提供的其他服务。在设计时上下文中,组件和它的容器之间的关系是通过站点
    建立的。在将组件放到窗体中时,设计器宿主会为该组件和它的容器创建一个站点实例。当此关系建立以后,组件
    已经被“站点化”,并使用它的 ISite 属性来访问它的容器所提供的服务。

            如下内容将以代码和相关注释为主,偶有说明穿插其中:
     
           我们将要建立的容器是 DiscuzPlugInContainer:
     
           public class DiscuzPlugInContainer:Container
           {
               public DiscuzPlugInContainer()
            {
            }

             为了以后对生成的ISITE进行所谓订制方便,我重写这Container 的 CreateSite方法(而.net框架中采用一个内部
    封装的类Site来进行站点的创建,它实现了ISite, IServiceProvider)接口。这里的DiscuzSite是一个自己定义的实现了
    ISITE接口的类:

           public ISite CreateSite(string name,IComponent component)
         {
             return CreateSite(component, name);
           }
      
         protected override ISite CreateSite(IComponent component,string name)
         {
             //此处代码可以对加载入容器的组件所生成的ISIETE进行接管,以实现我们自己的ISITE生成方式
             ISite site = base.CreateSite(component,name);
             if (site == null) //
             {
                 site= new DiscuzSite(component,this,name);
             }
               return site;
         }
     
           其中 DiscuzSite 的声明如下
        class DiscuzSite : ISite
     {
      private string name = "";
      private IComponent component;
      private DiscuzPlugInContainer container;
      public DiscuzSite(IComponent sitedComponent, DiscuzPlugInContainer site, string aName)
      {
       component = sitedComponent;
       container = site;
       name = aName;
      }
      public IComponent Component
      {
       get{ return component;}
      }
      public IContainer Container
      {
       get{return container;}
      }
      public bool DesignMode
      {
       get{return false;}
      }
      public string Name
      {
       get{ return name;}
       set{name=value;}
      }

      //支持IServiceProvider 接口.
      public virtual object GetService(Type service)
      {
       object s =this.container.Services.GetService(service);
       if (s == null)
       {
        return (object) null;
       }
       return s;
      }
     }
     
              相关要加入容器的组件类直接采用从Component类下继承的机制来实现(其实在实现的工作中这是不太好的用法,
    建议实现ICompnent接口来编写自己的组件)
              注:此处的IPlugin接口是一个插件接口,为了提供一个简单的示例应用才加入此接口。其要实现 RunPlugin方法
        public class PlugInComponent :Component,IPlugin 
     {
     
      public PlugInComponent():base()
      {}
     
      public PlugInComponent(string componentName)
      {
     
       _componentName = componentName;
       _innerName= componentName;
       Disposed = null;
       this.Connect+=new EventHandler(this.PlugInConnect); //此处加入了对事件的订制
       this.Close+=new EventHandler(this.PlugInClose); //此处加入了对事件的订制
      }
      
               代码中的Events属性使用的就是其实就是EventHandlerList events,这就决定了只要是从组件上继承就会有事件处
    理的支持。这里要注意的是。NET框架下的Component类的CanRaiseEvents属性。因为它在不被重写的情况下只会被返
    回true,
    如下:
      protected virtual bool CanRaiseEvents
      {
       get
       {
         return true;
       }
      }
             这在实际开发工作上能给我们提供一些方便。


              实现工作的用户组件定义如下,这里面的PluginInfo是用户定制属性,下面的PlugInConnect,PlugInClose函数是对
    父类PlugInComponent中的相关方法的重写。这里只为说明情况,因此在这里将把结果直接在网页中进行显示。

     [PluginInfo("UserPlugin" , "1.0" , "daizhj" , "http://nt,discuz.net/" , true ) ]
     class UserPlugin:PlugInComponent
     {
      
      public UserPlugin(string componentName):base(componentName)
      {
      }

      public override void PlugInConnect(object sender, EventArgs e)
      {
       System.Web.HttpContext.Current.Response.Write("<br> "+this.ComponentName+" user event connect ");
      }

      public override void PlugInClose(object sender, EventArgs e)
      {
       System.Web.HttpContext.Current.Response.Write("<br> "+this.ComponentName+" user event close ");
      }
     }


             下面的代码片段实现在向容器中加入服务,组件,及向组件绑定站点的过程。(这里把这些在功能上不相关的代
    码放在一起只为说明情况,在实现开发中如果这样写的话会挨板子的)
     
                 
       /// <summary>
      /// 组件服务测试
      /// </summary>
      public void PluginServiceTest()
      {
       //直接用实例加入服务
       this.Services.AddService(typeof(PlugMyService),new PlugMyService());

       //采用回调方法加入服务
                ServiceCreatorCallback sccb=new ServiceCreatorCallback(new CallbackMySerivce().CallBackRunMyService);
       sccb+=sccb;//因为下面要创建两个组件,如果要在代理列表上做++操作
       this.Services.AddService(typeof(CallbackMySerivce),sccb);
               
      

       PlugInComponent pic=new PlugInComponent("daizhenjun");
       this.Add((IComponent) pic ,"daizhenjun");
       ISite iss=CreateSite("daizhenjun", (IComponent) pic);
       pic.Site=iss;
       pic.RunPlugInService(); //运行相关的组件服务


       this.Services.RemoveService(typeof(CallbackMySerivce)); //移除服务
      

       PlugInComponent pic1=new PlugInComponent("daizhenjun2");
       this.Add((IComponent) pic1,"daizhenjun2");
       ISite iss1=CreateSite("daizhenjun2", (IComponent) pic1);
       pic1.Site=iss1;
       pic1.RunPlugInService(); //运行相关的组件服务
      }
      
             这里需要注意的一个对象是ServiceCreatorCallback 这个类其实说白了就是一个delegate,代码如下:
             public delegate object ServiceCreatorCallback(IServiceContainer container, Type serviceType);
             关于delegate用法和适用的场合在网上的资料多如牛毛,这里就不在说了。
       
             这里的RunPlugInService函数只是简单的返回基类中的服务对象(base.GetService(service) )   并对其进行简单
    调用而已,在实际工作中不建议这样做(我个人觉得这个功能应在服务中进行相关的封装,这样在组件中就可以
    根据自己的实际情况来决定享用(调用)那些服务了。

             其实在写上面代码时还有过一些思考:
             1.   如果获得某个容器内的站点数的问题,因为在.net框架中的Container类是用
      private int siteCount;来记录的,它会在当记录加入一个组件后(正常加入)进行"++"操作,如下:
      ISite site2 = this.CreateSite(component, name);
            this.sites[this.siteCount++] = site2;
            component.Site = site2;
      因为siteCount为私有,这就会造成外部无法直接访问,庆幸的是可以用Container.Components.Count来得到,因
    为站点与组件存在
      1:1的关系。

             2.   但要想获得容器中的服务数目可要难了,因为ServiceContainer中的存入服务的对象是一个私有的hashtable,
    而得到这个字段的属性竟然也是private类型,看来只有在容器中手工写入相关的代码才能进行服务数目的统计了。

       
        
             其它说明(只是提供一个相其简单的应用场景而已):
             PluginInheritTest();/// 插件继承事件运行测试
             LoadAndRunUserComponent();///安装并运行用户插件
      

             如下是参考的文章:
             http://www.urbanpotato.net/default.aspx/document/1757
       
             http://weblogs.asp.net/cazzu/archive/2004/05/10/129140.aspx
       
             另外在System.ComponentModel名空间下的一些其它类在园子里已有人对它们做了相关的说明并写出了一些
    使用示例代码。
             http://www.cnblogs.com/mapserver/category/65343.html
       
             这篇文章只是一些个人观点,如果大家(特别是精于此道之人)看到,希望能给出建议或相关链接。谢谢。
       
             代码下载链接:https://files.cnblogs.com/daizhj/Componentcode.rar
             (写的比较糙,大家多担待)

  • 相关阅读:
    iOS开发学习之NSFetchedResultsController
    iOS开发学习之Core Data
    iOS开发学习之MapKit
    iOS学习之CoreLocation相关知识
    制作LaunchImage的简单方法
    iOS学习之table view controller、table view cell
    iOS学习之NSKeyedArchiver、NSKeyedUnarchiver归档
    【MM 】委外加工退货
    【SAP Workflow】相关英语专业术语
    【MM】Purchasing Account Management---待完善
  • 原文地址:https://www.cnblogs.com/daizhj/p/597748.html
Copyright © 2020-2023  润新知