CodeModel:一个为项目所有相关类添加工厂方法的AddIn范例

1、问题描述

成都创新互联企业建站,十多年网站建设经验,专注于网站建设技术,精于网页设计,有多年建站和网站代运营经验,设计师为客户打造网络企业风格,提供周到的建站售前咨询和贴心的售后服务。对于成都网站制作、成都做网站中不同领域进行深入了解和探索,创新互联在网站建设中充分了解客户行业的需求,以灵动的思维在网页中充分展现,通过对客户行业精准市场调研,为客户提供的解决方案。

对一个程序做性能优化,发现程序里会大量创建动态对象,是影响性能的一个瓶颈。程序里都是采用Activator.CreateInstance(Type)的方法,记得在codeproject看过一篇文章(原文在此:Dynamic Objects, Factories, and Runtime Machines to Boost Performance),对动态创建对象的几种方式进行效率对比,Activator.CreateInstance是效率较低的一种。采用FormatterServices.GetUninitializedObject得到一个未初始化的类,再调用类的工厂方法,效率有大幅提高。下面是简单的代码说明。

 
 
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Runtime.Serialization;  
  5.  
  6.  
  7. //***种方式:Activator.CreateInstance(Type)方式。效率较低  
  8. namespace ActivatorCreator  
  9. {  
  10.     public class Widget { }  
  11.     public class WidgetA : Widget { }  
  12.     public class WidgetB : Widget { }  
  13.     //..  
  14.     public class WidgetZ : Widget { }  
  15.       
  16.     public abstract class Creator  
  17.     {  
  18.         public static Widget DynamicCreate(Widget w)  
  19.         {  
  20.             return (Widget)Activator.CreateInstance(w.GetType());  
  21.         }  
  22.     }  
  23. }  
  24. //第二种动态创建对象方式  
  25. //采用FormatterServices.GetUninitializedObject(Type)  
  26. //获得一个未初始化对象,再调用对象的工厂方法得到对象。  
  27. //经测试时间效率大概是前一种的150倍左右。  
  28. namespace SerializationCreator  
  29. {  
  30.     public class Widget  
  31.     {  
  32.         public virtual Widget GetInstance()  
  33.         {  
  34.             return new Widget();  
  35.         }  
  36.     }  
  37.     public class WidgetA : Widget  
  38.     {  
  39.         public override Widget GetInstance()  
  40.         {  
  41.             return new WidgetA();  
  42.         }  
  43.     }  
  44.     public class WidgetB : Widget  
  45.     {  
  46.         public override Widget GetInstance()  
  47.         {  
  48.             return new WidgetB();  
  49.         }  
  50.     }  
  51.     //  
  52.     public class WidgetZ : Widget  
  53.     {  
  54.         public override Widget GetInstance()  
  55.         {  
  56.             return new WidgetZ();  
  57.         }  
  58.     }  
  59.  
  60.     //Serialization配合工厂方法动态生成对象,高效率  
  61.     public abstract class Creator  
  62.     {  
  63.         public static Widget DynamicCreate(Widget w)  
  64.         {  
  65.             Widget widgetFactory =   
  66.                 (Widget)FormatterServices.GetUninitializedObject(w.GetType());  
  67.             return widgetFactory.GetInstance();  
  68.         }  
  69.     }  
  70. }  
  71.  

因此,决定采用FormatterServices.GetUninitializedObject方法来代替Activator.CreateInstance(Type)。整个项目有两个基类以及从该基类继承的大量子类(数目大概有100+)需要更改,而且继承关系最深达到5层。代码的修改工作量不小。

2、解决办法

2.1 复制粘贴

大概是最常规也最无趣的方法,我在复制了10个左右的类后实在受不了这种单调和枯燥,放弃了。

2.2 Code Snippet

复制的进阶,就是用Code Snippet了。把相同代码做成Snippet,效率有大幅提高。Snippet文件内容如下:

 
 
 
  1. < ?xml version="1.0" encoding="utf-8"?> 
  2. < CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
  3.   < CodeSnippet Format="1.0.0"> 
  4.     < Header> 
  5.       < Title>Add Factory Method< /Title> 
  6.       < Author>lumber< /Author> 
  7.       < Description>为类添加工厂方法< /Description> 
  8.       < HelpUrl>< /HelpUrl> 
  9.       < SnippetTypes /> 
  10.       < Keywords /> 
  11.       < Shortcut>fm< /Shortcut> 
  12.     < /Header> 
  13.     < Snippet> 
  14.       < References /> 
  15.       < Imports /> 
  16.       < Declarations> 
  17.         < Literal Editable="false"> 
  18.           < ID> RetType < /ID> 
  19.           < Type>< /Type> 
  20.           < ToolTip>返回的类型< /ToolTip> 
  21.           < Default> RetType < /Default> 
  22.      < !--注意这里的Function,以及ClassName()函数--> 
  23.           < Function>ClassName()< /Function> 
  24.         < /Literal> 
  25.       < /Declarations> 
  26.       < Code Language="csharp" Kind="" Delimiter="$"> 
  27.         < ![CDATA[internal override CADEntityData GetInstance()  
  28.       {  
  29.           return new $RetType$();  
  30.       }]]> 
  31.       < /Code> 
  32.     < /Snippet>x  
  33.   < /CodeSnippet> 
  34. < /CodeSnippets> 

在上面的snippet中,值得一提的是,使用了snippet function。即先定义了一个Literal,名称为RetType,代表工厂方法返回类型。我们知道不同的子类,工厂方法的函数签名相同,不同的是返回该类的实例。即RetType的值要等于被插入的类的名称。于是我们为RetType这个Literal提供了一个function,ClassName(),该函数返回snippet所在类的名称。

实际我们不需要手工来写这个文件,这里推荐Code Snippet Editor这个小工具。

2.3 CodeModel

现在只需要找到需要更改的类,敲下快捷键fm,再双击tab就ok了,委实比当初复制、编辑幸福多了。但人心难足,Snippet还是有不爽的地方:

1、找到并打开所有要修改的类(100+啊兄弟,项目里每个类都是一个单独文件)不停地重复按键,也是挺无聊的活。更重要的是,要保证不能遗漏——上文说了,很多子类都经过了多达5层的继承……

2、不同的基类要重新编制一个snippet。另外考虑到,如果以后别的项目要有类似的更改呢?如果项目是用VB.NET而不是c#呢?。。。。

看来,***的解决办法是写一个AddIn了。

简单分析下任务,其实就是两个:1、寻找项目中要添加工厂方法的基类及所有派生类。2、为这些类添加一个相应的工厂函数。

很自然需要用到CodeModel。

关于VS的扩展开发,推荐园子里Anders Cui的系列文章。而Anders Cui的系列恰好没有写关于CodeModel的内容,既有珠玉在前,所以小可也就斗胆续貂,简单写写关于CodeModel的内容。

直接用Vs2008新建项目,选择“Visual Stduio外接程序”,选择使用c#语言开发,然后一路默认,编辑器就自动为你生成了一个AddIn项目。该项目已经自动生成了大部分代码,包括将的AddIn程序添加到工具菜单上等。接下来只需要将需要执行的代码加入到Exec函数中即可。

我们再在项目中添加一个含文本框的窗体,作为输入界面,输入项目中要添加工厂方法的基类的全名(含命名空间)。接下来就是利用CodeModel寻找该类及其派生类,并添加工厂方法。还是代码说话吧,相关函数我在注释里都有说明,另外可以查询msdn。
    

 
 
 
  1. /// < summary>  
  2. /// 实现 IDTCommandTarget 接口的 Exec 方法。此方法在调用该命令时调用。编辑器自动生成。  
  3. /// < /summary>  
  4. /// < param term='commandName'>要执行的命令的名称。< /param>  
  5. /// < param term='executeOption'>描述该命令应如何运行。< /param>  
  6. /// < param term='varIn'>从调用方传递到命令处理程序的参数。< /param>  
  7. /// < param term='varOut'>从命令处理程序传递到调用方的参数。< /param>  
  8. /// < param term='handled'>通知调用方此命令是否已被处理。< /param>  
  9. /// < seealso class='Exec' />  
  10. public void Exec(string commandName, vsCommandExecOption executeOption,  
  11.     ref object varIn, ref object varOut, ref bool handled)  
  12. {  
  13.     handled = false;  
  14.     if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)  
  15.     {  
  16.         if (commandName == "AddFactoryMethod.Connect.AddFactoryMethod")  
  17.         {  
  18.             InputBox ibox = new InputBox();  
  19.             //程序添加一个输入窗口,输入要添加工厂方法的基类的全称(含命名空间)  
  20.             if (ibox.ShowDialog() == DialogResult.OK   
  21.                      && !string.IsNullOrEmpty(ibox.Str))  
  22.             {  
  23.                 //获取当前解决方案的***个项目的CodeModel  
  24.                 //注意CodeModel中集合都是以下标1开始而非0。如Projects.Item(1)  
  25.                 CodeModel cm = _applicationObject.Solution.Projects.Item(1).CodeModel;  
  26.                 foreach (CodeElement ce in cm.CodeElements)  
  27.                 {  
  28.                     //只有命名空间和类中才包含类。  
  29.                     if ((ce is CodeNamespace) | (ce is CodeClass))  
  30.                     {  
  31.  
  32.                         SetAllClass(ce, ibox.Str);  
  33.                     }  
  34.                 }  
  35.             }  
  36.  
  37.             handled = true;  
  38.             return;  
  39.         }  
  40.     }  
  41. }  
  42. /// < summary>  
  43. /// 寻找基类及所有子类,并添加工厂方法。  
  44. /// 这个函数主要演示了:  
  45. /// 1、如何寻找一个项目中包含的所有类。  
  46. /// 注意,所有的类应该包含在项目的命名空间和类中。  
  47. /// 2、如何判断类是指定类的派生类。  
  48. /// 3、CodeElement(包括CodeClass、CodeFunction……)等一系列对象的基本用法。  
  49. /// < /summary>  
  50. /// < param name="ct">类或者命名空间< /param>  
  51. /// < param name="str">基类的全称(含命名空间)< /param>  
  52. private void SetAllClass(CodeElement ct, string str)  
  53. {  
  54.     CodeClass cc = ct as CodeClass;  
  55.     if (cc != null && cc.get_IsDerivedFrom(str))  
  56.     {  
  57.         //cc.get_IsDerivedFrom(str)函数用来判断类是否由全名为str的类派生得来。  
  58.         //注意!自身也是自身的派生类,即cc.get_IsDerivedFrom(cc.FullName)将返回true。  
  59.         //为基类和派生类添加工厂方法  
  60.         AddFactoryMethod(cc, str);  
  61.     }  
  62.     CodeElements elements;  
  63.     if (ct is CodeNamespace)  
  64.     {  
  65.  
  66.         elements = (ct as CodeNamespace).Members;  
  67.     }  
  68.     else 
  69.     {  
  70.  
  71.         elements = (ct as CodeClass).Members;  
  72.     }  
  73.  
  74.     foreach (var ce in elements)  
  75.     {  
  76.         if ((ce is CodeNamespace) | (ce is CodeClass))  
  77.         {  
  78.             //寻找嵌套的类  
  79.             SetAllClass(ce as CodeElement, str);  
  80.         }  
  81.     }  
  82. }  
  83. /// < summary>  
  84. /// 为类添加工厂方法。  
  85. /// 这个函数主要演示了:  
  86. /// 1、如何用AddFunction方法为类添加方法。  
  87. /// 2、CodeModel和文档操作之间的结合。  
  88. /// code***.GetStartPoint/GetEndPoint可以获得TextPoint;  
  89. /// TextPoint.CodeElement可获得对应的codeElement。  
  90. /// 3、为不同的编程语言(VB.NET,c#)提供插件。  
  91. /// < /summary>  
  92. /// < param name="cc">要添加方法的类< /param>  
  93. /// < param name="fullname">基类的全称< /param>  
  94. private void AddFactoryMethod(CodeClass cc, string fullname)  
  95. {  
  96.  
  97.     string str1 = "";  
  98.     string str2 = "";  
  99.  
  100.     if (cc.Language == CodeModelLanguageConstants.vsCMLanguageCSharp)  
  101.     {  
  102.         str1 = string.Format("return new {0}();\n", fullname);  
  103.         if (cc.FullName == fullname)  
  104.             //基类  
  105.             str2 = "public virtual";  
  106.         else//子类  
  107.             str2 = "public override";  
  108.  
  109.     }  
  110.     else if (cc.Language == CodeModelLanguageConstants.vsCMLanguageVB)  
  111.     {  
  112.         str1 = string.Format("return new {0}()\n", fullname);  
  113.         if (cc.FullName == fullname)  
  114.             //基类  
  115.             str2 = "Public Overridable";  
  116.         else//子类  
  117.             str2 = "Public Overrides";  
  118.     }  
  119.     //添加函数  
  120.     CodeFunction cf = cc.AddFunction("GetInstance", vsCMFunction.vsCMFunctionFunction,  
  121.         fullname, -1, vsCMAccess.vsCMAccessPublic, null);  
  122.     //为函数添加文档注释  
  123.     cf.DocComment = "< summary>\n工厂方法生成一个实例。\n< /summary>";  
  124.     EditPoint ep = cf.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();  
  125.     //添加函数体  
  126.     ep.Insert(str1);  
  127.     ep = cf.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint();  
  128.     ep.ReplaceText(6, str2, 0);  
  129. }  
  130. private DTE2 _applicationObject;  

3、一些问题

3.1 CodeClass的属性DerivedTypes没有实现。否则寻找指定类的所有派生类将更为简化,程序更简单。

3.2 CodeClass.AddFunction方法,第2和第5个参数,使用示例代码以外的参数值,均会出现异常。Bug??所以示例***被迫使用ReplaceText来完成加入虚函数的任务。希望有高手指点一下。

3.3 CodeModel中大部分集合,特别是有item属性但item非默认属性者,使用下标索引时,从1而非0开始。EnvDTE命名空间中其他集合大概也是。或者,推而广之,涉及到VSTA开发的(如VSTO),集合一般下标从1开始。毕竟这些领域还是VB为主。

【编辑推荐】

  1. JavaScript设计模式之抽象工厂及工厂方法模式
  2. J2EE缩写名词解释
  3. J2EE运行环境性能大优化
  4. JSF能否拯救WEB
  5. Liferay Portal中的jBPM配置

分享标题:CodeModel:一个为项目所有相关类添加工厂方法的AddIn范例
转载来源:http://www.hantingmc.com/qtweb/news49/538299.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联