Net有道

紫冠道人的求道历程

导航

动态生成与编译(四)----用CodeDOM生成一个完整的类(上)

 

 

()里用一个求Fibonacci数列的程序来说明CodeDOM是如何生成一些程序的基本语句的。现在写程序很少会直接写几个方法来让Main()从头调到尾的,总是要用几个类来封装封装的。

CodeDOM里一个类的字段、属性、事件(讲到事件,委托总是逃不了的)、方法等又是如何来生成的呢?上次只讲到了类方法(CodeMemberMethod),在CodeTypeMember这个重量级的类下面还有很多没有涉及,这次用一个比较完整的类来讲余下的部分。

 

开始部分的几下CodeCompileUnitCodeNamespaceCodeNamespaceImport都是一样的,就不多说了。

声明一个名为DemoClass的类:

CodeTypeDeclaration MyClass = new CodeTypeDeclaration("DemoClass");

对一个Class来说除了名字外也没什么好设置的,一般情况下这样就行了。但有些定义了一些Attribute的要麻烦一点。要对MyClassCustomAttributes属性进行设置(不是MyClassAttributes这个属性,这个是设置可见性的)。

对于Attribute的生成CodeDOM有专门的类来实现,就是CodeAttributeDeclaration。如要对DemoClass这个类加一个[Description("CodeDOM自动生成的一个类")]这样的Attribute可以这样写:

                MyClass.CustomAttributes.Add(new CodeAttributeDeclaration("Description",new CodeAttributeArgument(

                 new CodePrimitiveExpression("CodeDOM自动生成的一个类"))));

从上可以看到CodeAttributeDeclaration的构造函数,第一个参数是Attribute的名,第二个参数是Attribute的参数。对Description来说参数是“CodeDOM自动生成的一个类”这么一个字符串。当然不能直接用这个字符串来当构造函数的参数。对于Attribute的参数又有一个专门的类的――CodeAttributeArgument,真是麻烦。还要把字符串包成一个CodeExpression去当CodeAttributeArgument的构造函数的参数才算了结。(最后那个CodePrimitiveExpression在(三)里讲到过了,它返回一个原始的表达式,实际上就是把括号里的东西类型转换成一个CodeExpressoin)。

 

(上面的长长的一句就是在写CodeDOM程序时构造函数连用的写法。当然你也可以不用这种new串联的写法,可以从最里层开始声明一个个的变量一句句的构造出来。上面一句就是分为四句了,不过这样写变量一多自己也要晕掉,所以一般不是太长的我还是喜欢连写,如果把代码格式编排一下一般写10new是没什么问题的。比较头痛的是在VS.NET中排好的格式到Word里会变掉)

 

好,一个类声明好了,下面对类里的元素一个一个来说。

先是字段Field,从它的类层次结构里能看出,这个肯定是用CodeMemberField来做的,如在类里声明

private int myField;

可以这样来写:

                CodeMemberField myField = new CodeMemberField("System.Int32","myField");

                //myField.Attributes = MemberAttributes.Private;

           MyClass.Members.Add(myField);

它的构造函数,前一个参数是类型,后一个参数是字段名。前一个参数有很多种写法,这在(三)里说CodeParameterDeclarationExpression的时候提到过了(构造函数有(Type,string)(string,string)(CodeTypeReference,string)这三种写法),在CodeDOM里只要构造函数有关于类型的一般都有上述的Type,string,CodeTypeReference三种写法。

Attribute对于字段来说默认是private。所以这里可以注释掉。

一般地上面几点就够了,如果字段声明的同时要初始化的,只要设一下InitExpression就是了,这跟变量声明不太一样的地方是,它没有相应的构造函数,只能先new出来后,再设置相应的InitExpression属性。下面四句

                CodeMemberField myArray = new CodeMemberField("System.Int32[]","myArray");

                myArray.Attributes = MemberAttributes.Private;

                myArray.InitExpression = new CodeArrayCreateExpression("System.Int32",10);

           MyClass.Members.Add(myArray);

产生private int[] myArray = new int[10];

 

数组比较的特别一点,声明的时候倒是容易,直接也在类型后加个“[]”就行了,就是初始化的时候麻烦一点,要用到数组创建表达式CodeArrayCreateExpression。它的用法参照一下对应的产生代码就明白了,构造函数都是大同小异的。

 

字段声明完,接着要写构造函数了。构造函数就是一个比较特殊的类方法,CodeConstructor就是从CodeMemberMethod继承下来声明构造函数用的。除了名字不太一样,用法与CodeMemberMethodCodeEntryPointMethod差不多,这里就不详述了。

 

下面是属性的写法:

                CodeMemberProperty MyProperty = new CodeMemberProperty();

                MyProperty.Name = "MyProperty";

                MyProperty.Type = new CodeTypeReference("System.Int32");

                MyProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;

                MyProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),"myField")));

                MyProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),"myField"),

                     new CodePropertySetValueReferenceExpression()));

           MyClass.Members.Add(MyProperty);

这么长长的一段出来

        public int MyProperty {

            get {

                return this.myField;

            }

            set {

                this.myField = value;

            }

        }

这个CodeMemberProperty从上往下看,就是依次设置属性名、类型、可见性,以及getset方法的语句。这个GetStatements.Add()(或SetStatements.Add())里就随你加了,简单的属性设置就如上面一样,复杂的话就不断地依续再往里Add就是了。

下面简单的讲一下GetSet里一些细节的东西。首先是return this.myField;这个是如何来的,上次讲Fibonacci数列时没讲到,返回值语句就是由CodeMethodReturnStatement产生的,要返回什么构造函数里就放什么,很简单。而this.myField就是类字段了,用CodeFieldReferenceExpression,不是上面的CodeMemberField,那个是声明类字段用的,这里是要引用类字段(跟变量声明与引用差不多)。同理CodePropertyReferenceExpression是就是用到类的属性时用了。在属性设置里“value”这个关键字会频繁的出现,在CodeDOM里对这个“value”也是有个专门的类的,就是CodePropertySetValueReferenceExpression了。比较特别的东西总是有个专门的类来表示,就象“this”就用CodeThisReferenceExpression来表示一样。

 

在类的属性里有个比较特殊的属性就是索引器属性,设置索引器属性的写法与设置一般的属性基本一样,只是把属性名改为“Item”就行了。如:

public int this[int index] {。。。。。略} 

这么一个索引器属性由下述的语句产生:

                CodeMemberProperty Pindex = new CodeMemberProperty();

                Pindex.Comments.Add(new CodeCommentStatement("索引器属性"));

                Pindex.Type = new CodeTypeReference("System.Int32");

                Pindex.Name = "Item";//属性名设为Item,CodeDOM就当作是索引器属性处理

                Pindex.Attributes = MemberAttributes.Public | MemberAttributes.Final;

           Pindex.Parameters.Add(new CodeParameterDeclarationExpression("System.Int32","index"));

。。。。下略

 

类属性的设置是够麻烦的,但最麻烦的要数类事件了,如果只是声明一下事件倒是简单得很,但讲到事件就不得不讲委托,这一下篇幅就长了。这留下次讲。

posted on 2004-11-02 13:56  lichdr  阅读(11469)  评论(4编辑  收藏  举报