【.net 深呼吸】细说CodeDom(4):类型定义

上一篇文章中说了命名空间,你猜猜接下来该说啥。是了,命名空间下面就是类型,知道了如何生成命名空间的定义代码,之后就该学会如何声明类型了。

CLR的类型通常有这么几种:类、接口、结构、枚举、委托。是这么几个,应该没有漏掉的吧。

定义类型,除了委托外都可以用 CodeTypeDeclaration 类完成。CodeNamespace类公开一个Types集合,定义的类型必须添加到这个集合中,才能与命名空间关联。

举个例子,下面代码将定义一个叫 Mouse 的类。

            // 编译单元
            CodeCompileUnit unit = new CodeCompileUnit();
            // 命名空间
            CodeNamespace nspace = new CodeNamespace();
            nspace.Name = "Sample";
            unit.Namespaces.Add(nspace);
            // 类声明
            CodeTypeDeclaration clsdcl = new CodeTypeDeclaration();
            clsdcl.Name = "Mouse";
            // 公共且密封,不让继承
            clsdcl.TypeAttributes = System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Public;
            // 加入类型集合中
            nspace.Types.Add(clsdcl);

            CodeDomProvider prd = CodeDomProvider.CreateProvider("cs");
            prd.GenerateCodeFromCompileUnit(unit, Console.Out, null);

不需要显式把 IsClass 说罢为 true,因为默认就是生成类(class)的。这里有一点老周要说明一下。

描述类型的可访问性有两个属性可以用,一个是从 CodeTypeMember 类继承的 Attributes,由于MemberAttributes枚举不能进行组合运用(我想把类定义为 public sealed),于是我就选用了TypeAttributes,它用的值是反射里面的TypeAttributes枚举,这个枚举可以多个值组合运用。

生成的代码如下图所示。

下面代码将定义一个名为 Point 的结构。

            CodeTypeDeclaration strdcl = new CodeTypeDeclaration("Point");
            strdcl.IsStruct = true;
            strdcl.TypeAttributes = System.Reflection.TypeAttributes.NotPublic;
            nspace.Types.Add(strdcl);

NotPublic表示类型可访问性为internal,即仅限当前程序集可见。生成代码如下图所示。

知道怎么定义类和结构后,那么枚举就难不倒你了。

            CodeTypeDeclaration endcl = new CodeTypeDeclaration("AccessType");
            endcl.IsEnum = true;
            nspace.Types.Add(endcl);

生成代码如下。

但是,大伙伴们一定会问,那委托呢。CodeTypeDeclaration类并不能用来声明委托类型,但它派生出了一个CodeTypeDelegate类,它是定义委托类型专业户。

我们知道,委托类似于方法,那么你想想,委托类型需要几个要素。首先,参数列表要吧。然后,还得有个返回值。好,还是实际效果有用,不多说,来,先定义一个委托类型试试手。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Calculators");
            unit.Namespaces.Add(ns);

            CodeTypeDelegate dl = new CodeTypeDelegate("OnAdd");
            // 返值为int类型
            dl.ReturnType = new CodeTypeReference(typeof(int));
            // 两个参数
            CodeParameterDeclarationExpression p1 = new CodeParameterDeclarationExpression();
            p1.Name = "x";
            p1.Type = new CodeTypeReference(typeof(int));
            CodeParameterDeclarationExpression p2 = new CodeParameterDeclarationExpression();
            p2.Name = "y";
            p2.Type = new CodeTypeReference(typeof(int));
            dl.Parameters.Add(p1);
            dl.Parameters.Add(p2);
            ns.Types.Add(dl);

CodeTypeReference用于生成类型引用代码,可以用Type对象提供类型信息,也可以用字符串来提供。方法参数可以用CodeParameterDeclarationExpression类来声明,核心元素是参数类型与参数名。

如果委托没有参数,就不用向Parameters集合添加任何东西,有几个参数就加几个。

最后生成的委托类型代码如下。

下面代码生成一个无参数并返回void的委托类型。

            CodeTypeDelegate dl2 = new CodeTypeDelegate("DoWork");
            dl2.ReturnType = new CodeTypeReference(typeof(void));
            ns.Types.Add(dl2);

生成的委托类型为

============================================

此时大家可能会想到,类型之间存在继承关系,比如类与类之间,类/结构可以实现接口。

下面代码声明了两个类——A、B,其中B从A派生。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Samples");
            unit.Namespaces.Add(ns);

            CodeTypeDeclaration t1 = new CodeTypeDeclaration("A");
            CodeTypeDeclaration t2 = new CodeTypeDeclaration("B");
            // B 从 A 派生
            t2.BaseTypes.Add(new CodeTypeReference(t1.Name));
            ns.Types.AddRange(new CodeTypeDeclaration[] { t1, t2 });

CodeTypeDeclaration 类有一个 BaseTypes 集合,用来设置该类型的基类。

这里你必须一个问题:虽然 BaseTypes 是一个集合,可以添加N个类型引用,可是你得遵守.NET的面向对象规则,即类不能多继承,但类型可以实现多个接口。

尽管你可以这么搞:

            t2.BaseTypes.Add(new CodeTypeReference(t1.Name));
            t2.BaseTypes.Add(new CodeTypeReference(typeof(string)));

然后还生成了这样的代码

然而,你就要想一想了,这样的代码能否通过编译,不兴趣的朋友不妨试试。

想试试吗,好,老周就献丑了,列位看官莫笑。

            CodeDomProvider prd = CodeDomProvider.CreateProvider("c#");
            // 生成代码
            prd.GenerateCodeFromCompileUnit(unit, Console.Out, null);
            CompilerParameters options = new CompilerParameters();
            // 输出文件
            options.OutputAssembly = "test.dll";
            // 引用的程序集
            options.ReferencedAssemblies.Add("System.dll");
            // 开始编译
            CompilerResults res = prd.CompileAssemblyFromDom(options, unit);

            // 200% 出错
            if (res.Errors.Count == 0)
            {
                Console.WriteLine("编译完成。");
                Console.WriteLine($"输出程序集:{res.CompiledAssembly.Location}");
            }
            else
            {
                foreach (CompilerError er in res.Errors)
                {
                    Console.WriteLine($"{er.ErrorNumber} - {er.ErrorText}");
                }
            }

然后,一编译,就有好戏看了。

这告诉你,类A的基类不能有多个类。

下面再看看如何实现多个接口。

            CodeCompileUnit unit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Commons");
            unit.Namespaces.Add(ns);

            // 这两个都是接口
            CodeTypeDeclaration t1 = new CodeTypeDeclaration("IBall");
            t1.IsInterface = true;
            CodeTypeDeclaration t2 = new CodeTypeDeclaration("IPlayer");
            t2.IsInterface = true;
            ns.Types.Add(t1);
            ns.Types.Add(t2);

            // 这个是类,实现上面两个接口
            CodeTypeDeclaration t3 = new CodeTypeDeclaration("FootballPlayer");
            t3.BaseTypes.Add(new CodeTypeReference(t1.Name));
            t3.BaseTypes.Add(new CodeTypeReference(t2.Name));
            ns.Types.Add(t3);

t1和t2都是接口类型,t3是类,它将实现前面两个接口。运行后会生成以下代码。

OK,对于类型定义,今天就讲这么多吧。顺着这个层次,下一篇文章,老周就会说说类型成员的定义。

开饭了,开饭了……

时间: 2024-10-12 03:08:16

【.net 深呼吸】细说CodeDom(4):类型定义的相关文章

【.net 深呼吸】细说CodeDom(2):表达式、语句

在上一篇文章中,老周厚着脸皮给大伙介绍了代码文档的基本结构,以及一些代码对象与CodeDom类型的对应关系. 在评论中老周看到有朋友提到了 Emit,那老周就顺便提一下.严格上说,Emit并不是针对代码文档生成和编译而设计的,Emit一方面可以实时发出 IL 指令,另一方面也支持动态程序集,即可以在运行时创建程序集,并可以定义类型,然后可以执行.而CodeDom所针对的是代码文档的生成和编译,所以说,是有所不同的. 哦,是了,还有一个玩意儿挺有趣,也提一下吧——动态 Linq 表达式树.它也跟动

DTD文档类型定义文件简介

dtd 基本概念: dtd ( document type definition  文档类型定义),该文件一般和xml文件配合使用, 主要的用处是约束 xml. 除了 dtd 技术外, 还有一个schema的技术也可以用于约束xml文件的书写规范. 现在请看一个问题: <stu id="a"0&apos;0&apos;1<" > <name>杨过</name> <sex>男</sex> <

[C语言]进阶|结构类型: 枚举, 结构, 类型定义

--------------------------------------------------------------------- 枚举: // main.c #include <stdio.h> const int red = 0; const int yellow = 1; const int green = 2; int main(int argc, const char * argv[]) { /* 常量符号化:用符号而不是具体的数字来表示程序中的数字; 这样看到符号才能知道背

宏不是类型定义

#include <iostream> #include <iomanip> #include <string> using namespace std; struct CarType { string maker; int year; float price; }; //使用这样的宏定义还有着可移植性的优势,所有的C编译器都支持它. #define EXAMPLE1 struct CarType * //很多编译器不支持如下方法 typedef struct CarT

DOCTYPE html PUBLIC 指定了 HTML 文档遵循的文档类型定义

DOCTYPE html PUBLIC 指定了 HTML 文档遵循的文档类型定义 今天看到一篇CSS应用的一个友好搜索,我按网页上的代码复制.粘贴后预览时总达不到效果,而直接拷贝他的实例却能达到效果,开始以为书写顺序不对,于是调整书写顺序,还是不行,最后找到了我认为最没什么用处的第一行才发现真正起作用的竟然是 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http//www.w3.org

有效的XML: DTD(文档类型定义)介绍(转)

文档类型定义和命名空间 有效(Valid)的XML文档: 首先,XML文档是个格式正规的(Well-formed)XML文档:(见格式正规的XML:语法 属性 实体 处理指令 样式单 CDATA节). 其次,需要满足DTD的要求,这样的XML文档称为有效的(Valid)XML文档. DTD DTD,即文档类型定义——Document Type Definition. DTD用来描述XML文档的结构, 一个DTD文档包含: 元素(ELEMENT)的定义规则: 元素之间的关系规则: 属性(ATTLI

在 Typescript 2.0 中使用 @types 类型定义

在 Typescript 2.0 中使用 @type 类型定义 基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义.导致在编译的时候,经常会看到一连串的找不到类型的提示.解决的方式经过了许多的变化,从 DefinitelyTyped 到 typings.最后是 @types.在 Typescript 2.0 之后,推荐使用 @types 方式. DefinitelyTyped 这个工具已经不被推荐,仅作介绍. 多数来自 javascript 的库是没有 TypeScript 类

网易云课堂_C语言程序设计进阶_第三周:结构:结构、类型定义、联合

3.1 枚举 3.2 结构 3.3 类型定义 3.1 枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来表明: enum 枚举类型名字{名字0,...,名字n}; 枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0到n.如: enum colors{red, yellow, green}; 就创建了三个常量,red的值是0,yellow的值是1,而green的值是2 当需要一些可以排列起来的常量值时,定义枚举的意义就是给

XML之DTD(文档类型定义)

文档类型定义(DTD)可定义合法的XML文档构建模块.它使用一系列合法的元素来定义文档的结构. DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用. 声明元素 在 DTD 中,XML 元素通过元素声明来进行声明.元素声明使用下面的语法: <!ELEMENT 元素名称 类别>  [类别可为:EMPTY.(#PCDATA).ANY] 或者 <!ELEMENT 元素名称 (元素内容)> 带有 DTD 的 XML 文档实例 <?xml version="1.0&