新聞中心
1、問題描述

成都創(chuàng)新互聯(lián)企業(yè)建站,十多年網(wǎng)站建設(shè)經(jīng)驗,專注于網(wǎng)站建設(shè)技術(shù),精于網(wǎng)頁設(shè)計,有多年建站和網(wǎng)站代運營經(jīng)驗,設(shè)計師為客戶打造網(wǎng)絡(luò)企業(yè)風(fēng)格,提供周到的建站售前咨詢和貼心的售后服務(wù)。對于成都網(wǎng)站制作、成都做網(wǎng)站中不同領(lǐng)域進行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設(shè)中充分了解客戶行業(yè)的需求,以靈動的思維在網(wǎng)頁中充分展現(xiàn),通過對客戶行業(yè)精準市場調(diào)研,為客戶提供的解決方案。
對一個程序做性能優(yōu)化,發(fā)現(xiàn)程序里會大量創(chuàng)建動態(tài)對象,是影響性能的一個瓶頸。程序里都是采用Activator.CreateInstance(Type)的方法,記得在codeproject看過一篇文章(原文在此:Dynamic Objects, Factories, and Runtime Machines to Boost Performance),對動態(tài)創(chuàng)建對象的幾種方式進行效率對比,Activator.CreateInstance是效率較低的一種。采用FormatterServices.GetUninitializedObject得到一個未初始化的類,再調(diào)用類的工廠方法,效率有大幅提高。下面是簡單的代碼說明。
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Runtime.Serialization;
- //***種方式:Activator.CreateInstance(Type)方式。效率較低
- namespace ActivatorCreator
- {
- public class Widget { }
- public class WidgetA : Widget { }
- public class WidgetB : Widget { }
- //..
- public class WidgetZ : Widget { }
- public abstract class Creator
- {
- public static Widget DynamicCreate(Widget w)
- {
- return (Widget)Activator.CreateInstance(w.GetType());
- }
- }
- }
- //第二種動態(tài)創(chuàng)建對象方式
- //采用FormatterServices.GetUninitializedObject(Type)
- //獲得一個未初始化對象,再調(diào)用對象的工廠方法得到對象。
- //經(jīng)測試時間效率大概是前一種的150倍左右。
- namespace SerializationCreator
- {
- public class Widget
- {
- public virtual Widget GetInstance()
- {
- return new Widget();
- }
- }
- public class WidgetA : Widget
- {
- public override Widget GetInstance()
- {
- return new WidgetA();
- }
- }
- public class WidgetB : Widget
- {
- public override Widget GetInstance()
- {
- return new WidgetB();
- }
- }
- //
- public class WidgetZ : Widget
- {
- public override Widget GetInstance()
- {
- return new WidgetZ();
- }
- }
- //Serialization配合工廠方法動態(tài)生成對象,高效率
- public abstract class Creator
- {
- public static Widget DynamicCreate(Widget w)
- {
- Widget widgetFactory =
- (Widget)FormatterServices.GetUninitializedObject(w.GetType());
- return widgetFactory.GetInstance();
- }
- }
- }
因此,決定采用FormatterServices.GetUninitializedObject方法來代替Activator.CreateInstance(Type)。整個項目有兩個基類以及從該基類繼承的大量子類(數(shù)目大概有100+)需要更改,而且繼承關(guān)系最深達到5層。代碼的修改工作量不小。
2、解決辦法
2.1 復(fù)制粘貼
大概是最常規(guī)也最無趣的方法,我在復(fù)制了10個左右的類后實在受不了這種單調(diào)和枯燥,放棄了。
2.2 Code Snippet
復(fù)制的進階,就是用Code Snippet了。把相同代碼做成Snippet,效率有大幅提高。Snippet文件內(nèi)容如下:
- < ?xml version="1.0" encoding="utf-8"?>
- < CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
- < CodeSnippet Format="1.0.0">
- < Header>
- < Title>Add Factory Method< /Title>
- < Author>lumber< /Author>
- < Description>為類添加工廠方法< /Description>
- < HelpUrl>< /HelpUrl>
- < SnippetTypes />
- < Keywords />
- < Shortcut>fm< /Shortcut>
- < /Header>
- < Snippet>
- < References />
- < Imports />
- < Declarations>
- < Literal Editable="false">
- < ID> RetType < /ID>
- < Type>< /Type>
- < ToolTip>返回的類型< /ToolTip>
- < Default> RetType < /Default>
- < !--注意這里的Function,以及ClassName()函數(shù)-->
- < Function>ClassName()< /Function>
- < /Literal>
- < /Declarations>
- < Code Language="csharp" Kind="" Delimiter="$">
- < ![CDATA[internal override CADEntityData GetInstance()
- {
- return new $RetType$();
- }]]>
- < /Code>
- < /Snippet>x
- < /CodeSnippet>
- < /CodeSnippets>
在上面的snippet中,值得一提的是,使用了snippet function。即先定義了一個Literal,名稱為RetType,代表工廠方法返回類型。我們知道不同的子類,工廠方法的函數(shù)簽名相同,不同的是返回該類的實例。即RetType的值要等于被插入的類的名稱。于是我們?yōu)镽etType這個Literal提供了一個function,ClassName(),該函數(shù)返回snippet所在類的名稱。
實際我們不需要手工來寫這個文件,這里推薦Code Snippet Editor這個小工具。
2.3 CodeModel
現(xiàn)在只需要找到需要更改的類,敲下快捷鍵fm,再雙擊tab就ok了,委實比當初復(fù)制、編輯幸福多了。但人心難足,Snippet還是有不爽的地方:
1、找到并打開所有要修改的類(100+啊兄弟,項目里每個類都是一個單獨文件)不停地重復(fù)按鍵,也是挺無聊的活。更重要的是,要保證不能遺漏——上文說了,很多子類都經(jīng)過了多達5層的繼承……
2、不同的基類要重新編制一個snippet。另外考慮到,如果以后別的項目要有類似的更改呢?如果項目是用VB.NET而不是c#呢?。。。。
看來,***的解決辦法是寫一個AddIn了。
簡單分析下任務(wù),其實就是兩個:1、尋找項目中要添加工廠方法的基類及所有派生類。2、為這些類添加一個相應(yīng)的工廠函數(shù)。
很自然需要用到CodeModel。
關(guān)于VS的擴展開發(fā),推薦園子里Anders Cui的系列文章。而Anders Cui的系列恰好沒有寫關(guān)于CodeModel的內(nèi)容,既有珠玉在前,所以小可也就斗膽續(xù)貂,簡單寫寫關(guān)于CodeModel的內(nèi)容。
直接用Vs2008新建項目,選擇“Visual Stduio外接程序”,選擇使用c#語言開發(fā),然后一路默認,編輯器就自動為你生成了一個AddIn項目。該項目已經(jīng)自動生成了大部分代碼,包括將的AddIn程序添加到工具菜單上等。接下來只需要將需要執(zhí)行的代碼加入到Exec函數(shù)中即可。
我們再在項目中添加一個含文本框的窗體,作為輸入界面,輸入項目中要添加工廠方法的基類的全名(含命名空間)。接下來就是利用CodeModel尋找該類及其派生類,并添加工廠方法。還是代碼說話吧,相關(guān)函數(shù)我在注釋里都有說明,另外可以查詢msdn。
- /// < summary>
- /// 實現(xiàn) IDTCommandTarget 接口的 Exec 方法。此方法在調(diào)用該命令時調(diào)用。編輯器自動生成。
- /// < /summary>
- /// < param term='commandName'>要執(zhí)行的命令的名稱。< /param>
- /// < param term='executeOption'>描述該命令應(yīng)如何運行。< /param>
- /// < param term='varIn'>從調(diào)用方傳遞到命令處理程序的參數(shù)。< /param>
- /// < param term='varOut'>從命令處理程序傳遞到調(diào)用方的參數(shù)。< /param>
- /// < param term='handled'>通知調(diào)用方此命令是否已被處理。< /param>
- /// < seealso class='Exec' />
- public void Exec(string commandName, vsCommandExecOption executeOption,
- ref object varIn, ref object varOut, ref bool handled)
- {
- handled = false;
- if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
- {
- if (commandName == "AddFactoryMethod.Connect.AddFactoryMethod")
- {
- InputBox ibox = new InputBox();
- //程序添加一個輸入窗口,輸入要添加工廠方法的基類的全稱(含命名空間)
- if (ibox.ShowDialog() == DialogResult.OK
- && !string.IsNullOrEmpty(ibox.Str))
- {
- //獲取當前解決方案的***個項目的CodeModel
- //注意CodeModel中集合都是以下標1開始而非0。如Projects.Item(1)
- CodeModel cm = _applicationObject.Solution.Projects.Item(1).CodeModel;
- foreach (CodeElement ce in cm.CodeElements)
- {
- //只有命名空間和類中才包含類。
- if ((ce is CodeNamespace) | (ce is CodeClass))
- {
- SetAllClass(ce, ibox.Str);
- }
- }
- }
- handled = true;
- return;
- }
- }
- }
- /// < summary>
- /// 尋找基類及所有子類,并添加工廠方法。
- /// 這個函數(shù)主要演示了:
- /// 1、如何尋找一個項目中包含的所有類。
- /// 注意,所有的類應(yīng)該包含在項目的命名空間和類中。
- /// 2、如何判斷類是指定類的派生類。
- /// 3、CodeElement(包括CodeClass、CodeFunction……)等一系列對象的基本用法。
- /// < /summary>
- /// < param name="ct">類或者命名空間< /param>
- /// < param name="str">基類的全稱(含命名空間)< /param>
- private void SetAllClass(CodeElement ct, string str)
- {
- CodeClass cc = ct as CodeClass;
- if (cc != null && cc.get_IsDerivedFrom(str))
- {
- //cc.get_IsDerivedFrom(str)函數(shù)用來判斷類是否由全名為str的類派生得來。
- //注意!自身也是自身的派生類,即cc.get_IsDerivedFrom(cc.FullName)將返回true。
- //為基類和派生類添加工廠方法
- AddFactoryMethod(cc, str);
- }
- CodeElements elements;
- if (ct is CodeNamespace)
- {
- elements = (ct as CodeNamespace).Members;
- }
- else
- {
- elements = (ct as CodeClass).Members;
- }
- foreach (var ce in elements)
- {
- if ((ce is CodeNamespace) | (ce is CodeClass))
- {
- //尋找嵌套的類
- SetAllClass(ce as CodeElement, str);
- }
- }
- }
- /// < summary>
- /// 為類添加工廠方法。
- /// 這個函數(shù)主要演示了:
- /// 1、如何用AddFunction方法為類添加方法。
- /// 2、CodeModel和文檔操作之間的結(jié)合。
- /// code***.GetStartPoint/GetEndPoint可以獲得TextPoint;
- /// TextPoint.CodeElement可獲得對應(yīng)的codeElement。
- /// 3、為不同的編程語言(VB.NET,c#)提供插件。
- /// < /summary>
- /// < param name="cc">要添加方法的類< /param>
- /// < param name="fullname">基類的全稱< /param>
- private void AddFactoryMethod(CodeClass cc, string fullname)
- {
- string str1 = "";
- string str2 = "";
- if (cc.Language == CodeModelLanguageConstants.vsCMLanguageCSharp)
- {
- str1 = string.Format("return new {0}();\n", fullname);
- if (cc.FullName == fullname)
- //基類
- str2 = "public virtual";
- else//子類
- str2 = "public override";
- }
- else if (cc.Language == CodeModelLanguageConstants.vsCMLanguageVB)
- {
- str1 = string.Format("return new {0}()\n", fullname);
- if (cc.FullName == fullname)
- //基類
- str2 = "Public Overridable";
- else//子類
- str2 = "Public Overrides";
- }
- //添加函數(shù)
- CodeFunction cf = cc.AddFunction("GetInstance", vsCMFunction.vsCMFunctionFunction,
- fullname, -1, vsCMAccess.vsCMAccessPublic, null);
- //為函數(shù)添加文檔注釋
- cf.DocComment = "< summary>\n工廠方法生成一個實例。\n< /summary>";
- EditPoint ep = cf.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
- //添加函數(shù)體
- ep.Insert(str1);
- ep = cf.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint();
- ep.ReplaceText(6, str2, 0);
- }
- private DTE2 _applicationObject;
3、一些問題
3.1 CodeClass的屬性DerivedTypes沒有實現(xiàn)。否則尋找指定類的所有派生類將更為簡化,程序更簡單。
3.2 CodeClass.AddFunction方法,第2和第5個參數(shù),使用示例代碼以外的參數(shù)值,均會出現(xiàn)異常。Bug??所以示例***被迫使用ReplaceText來完成加入虛函數(shù)的任務(wù)。希望有高手指點一下。
3.3 CodeModel中大部分集合,特別是有item屬性但item非默認屬性者,使用下標索引時,從1而非0開始。EnvDTE命名空間中其他集合大概也是。或者,推而廣之,涉及到VSTA開發(fā)的(如VSTO),集合一般下標從1開始。畢竟這些領(lǐng)域還是VB為主。
【編輯推薦】
- JavaScript設(shè)計模式之抽象工廠及工廠方法模式
- J2EE縮寫名詞解釋
- J2EE運行環(huán)境性能大優(yōu)化
- JSF能否拯救WEB
- Liferay Portal中的jBPM配置
網(wǎng)站標題:CodeModel:一個為項目所有相關(guān)類添加工廠方法的AddIn范例
文章出自:http://m.5511xx.com/article/cohocid.html


咨詢
建站咨詢
