这么简单的一个类,其实直接让它生成类代码看一下也就知道行不行了。但既然做就做到底,把测试用的代码也一并用CodeDOM来生成算了,而且CodeDOM里还有几个类也要随便说一下。
先看生成后的代码(直接在原namespace里新做了一个类):
public class TestClass {
public static void Main() {
DemoClass a = new DemoClass();
DemoClass b = new DemoClass(123);
Console.WriteLine("MyConst = {0}", DemoClass.MyConst);
Console.WriteLine("a.MyProperty = {0}", a.MyProperty);
Console.WriteLine("b.MyProperty = {0}", b.MyProperty);
a.MyMethod();
// 附加委托
a.MyEvent += new myEventHandler(TestClass.MyHandler);
a.MyMethod();
// 移除委托
a.MyEvent -= new myEventHandler(TestClass.MyHandler);
a.MyMethod();
a[5] = (a[3] = 11);
for (int i = 0; (i < 10); i = (i + 1)) {
Console.WriteLine("a[{0}] = {1}", i, a[i]);
}
Console.Read();
}
static void MyHandler(object sender, myEventArgs e) {
Console.WriteLine(e.Dt.ToString());
}
}
现在虽然看CodeDOM程序时虽能在脑海里浮现生成的相应代码了,但CodeDOM程序还写得不熟,如果不对着生成的代码来写CodeDOM程序还是有点累。现在一般都要先写好目标代码的大致形状,再反过来写CodeDOM程序,现在也照上面生成的代码来分析要写的CodeDOM程序。
CodeTypeDeclaration TestClass = new CodeTypeDeclaration("TestClass");
CodeEntryPointMethod Start = new CodeEntryPointMethod();
开始这两句总归是少了的,下面两句声明变量a、b的只讲一句(第二句稍复杂,就讲它了),变量声明的以前讲到过了。如果不初始化变量倒是简单:
CodeVariableDeclarationStatement classb = new CodeVariableDeclarationStatement("DemoClass","b")
就OK,后面那个new DemoClass(123)一加就烦了,要设置classb的InitExpression(或者在构造函数里传第三个参数,效果一样的)。一看New什么什么的,下面要用上类的构造函数了,在CodeDOM里创建一个类的新实例的表达式用
public CodeObjectCreateExpression(
string createType,
params CodeExpression[] parameters
);
第一个参数是要创建的类型,第二个参数是构造函数的参数,当然没有参数就省略了,上面的CodeDOM程序加上
classb.InitExpression = new CodeObjectCreateExpression("DemoClass",new odePrimitiveExpression(123))
后就完成第二句变量b的声明了。
下面三句输出控制台的语句就是方法调用,这个是很熟的了,一开始就用过的,当然是用CodeMethodInvokeExpression了。它的参数的DemoClass.MyConst与 a.MyProperty这两个分别就是类的字段、属性依次用CodeFieldReferenceExpression、CodePropertyReferenceExpression也很简单的。只看一下第二句的写法:
new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Console"),"WriteLine",
new CodeExpression[] {new CodePrimitiveExpression("a.MyProperty = {0}"),new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("a"),"MyProperty")});
(我喜欢这种长长的写法,不用去声明一串的变量,这样一眼就能看出来产生的代码是什么。只要输的时候左括号、右括号先对好就不太会出错)
接下来几句是看事件的效果的,先调一下MyMethod,加委托后再调一次,移除委托后最后调一次。主要是附加与移除委托的这两句。
先看new myEventHandler(TestClass.MyHandler)这一句是如何产生的,又是一个new,不过它不是类实例的创建,当然用CodeObjectCreateExpression来写,最后的代码效果是一样的,不过还是不要那样写的好。创建委托用
public CodeDelegateCreateExpression(
CodeTypeReference delegateType,
CodeExpression targetObject,
string methodName
);
所以CodeDelegateCreateExpression delecra = new CodeDelegateCreateExpression(。。。略);
后面的委托new好了,下面就是加上去的事:
CodeAttachEventStatement attevent = new CodeAttachEventStatement(
new CodeVariableReferenceExpression("a"),"MyEvent",delecra);
是附加,那
CodeRemoveEventStatement removevent = new CodeRemoveEventStatement(
new CodeVariableReferenceExpression("a"),"MyEvent",delecra);
就是移除了。
最后看一下类的索引器属性。a[5] = (a[3] = 11);这一句连续赋值的,先看a[5]是如何来的,这实际就是数组里的东西是如何取的问题啦,CodeDOM有一个CodeArrayIndexerExpression刚好可以派上用场。new CodeArrayIndexerExpression(new CodeVariableReferenceExpression("a"),new CodePrimitiveExpression(5))就会产生a[5],很明显前一个参数是索引器的对象,后一个参数是具体的索引数。
下面的连续赋值, CodeDOM生成a[5]=(a[3]=11)这样看起来还是比较的清楚。它就是一个CodeAssignStatement,左项是a[5],而右项(a[3]=11)就是一个运算表达式CodeBinaryOperatorExpression,只不过它的运算符是赋值运算符而已。其实生成(a[5]=a[3])=11也是一样的。
不过上面的这两种写法要写的CodeDOM代码复杂得要命,还不如分成两句赋值语句写来得省事。
后面的一个循环没什么,最简单基本的东西,在(三)里提过了,略过。
下面那个供委托用的“MyHandler”方法只有一句方法调用,也没什么新东西。
用上面的CodeDOM程序生成的代码文件编译产生EXE文件,双击执行,在控制台输出如下:
Instance constructor1
Instance constructor2
MyConst = 12
a.MyProperty = 0
b.MyProperty = 123
DemoClass.MyMethod
DemoClass.MyMethod
Invokes MyEvent
2004-11-04 21:20:50 -------这里当然与程序执行时间有关
DemoClass.MyMethod
a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 11
a[4] = 0
a[5] = 11
a[6] = 0
a[7] = 0
a[8] = 0
a[9] = 0
看来CodeDOM生成的那个类,及后面用来测试它的代码正常的工作。