【.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 == )
{
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,对于类型定义,今天就讲这么多吧。顺着这个层次,下一篇文章,老周就会说说类型成员的定义。
开饭了,开饭了……
【.net 深呼吸】细说CodeDom(4):类型定义的更多相关文章
- 【.net 深呼吸】细说CodeDom(2):表达式、语句
在上一篇文章中,老周厚着脸皮给大伙介绍了代码文档的基本结构,以及一些代码对象与CodeDom类型的对应关系. 在评论中老周看到有朋友提到了 Emit,那老周就顺便提一下.严格上说,Emit并不是针对代 ...
- 编译器开发系列--Ocelot语言4.类型定义的检查
这里主要介绍一下检查循环定义的结构体.联合体.是对成员中包含自己本身的结构体.联合体进行检查.所谓"成员中包含自己本身",举例来说,就是指下面这样的定义. struct point ...
- 在 Typescript 2.0 中使用 @types 类型定义
在 Typescript 2.0 中使用 @type 类型定义 基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义.导致在编译的时候,经常会看到一连串的找不到类型的提示.解决的方式经 ...
- 1.4.2 solr字段类型--(1.4.2.1)字段类型定义和字段类型属性
1.4.2 solr字段类型 (1.4.2.1) 字段类型定义和字段类型属性. (1.4.2.2) solr附带的字段类型 (1.4.2.3) 使用货币和汇率 (1.4.2.4) 使用Dates(日期 ...
- DOCTYPE html PUBLIC 指定了 HTML 文档遵循的文档类型定义
DOCTYPE html PUBLIC 指定了 HTML 文档遵循的文档类型定义 今天看到一篇CSS应用的一个友好搜索,我按网页上的代码复制.粘贴后预览时总达不到效果,而直接拷贝他的实例却能达到效果, ...
- C语言学习笔记--类型定义&联合
一.类型定义 C语言自定义数据类型 (typedef) C语言提供一个叫做typedef的功能来声明一个已有的数据类型的新名字. typedef int Length; 使得Length成为int类型 ...
- 网易云课堂_C语言程序设计进阶_第三周:结构:结构、类型定义、联合
3.1 枚举 3.2 结构 3.3 类型定义 3.1 枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来表明: enum 枚举类型名字{名字0,...,名字n}; 枚举类型名字通常并不 ...
- 有效的XML: DTD(文档类型定义)介绍(转)
文档类型定义和命名空间 有效(Valid)的XML文档: 首先,XML文档是个格式正规的(Well-formed)XML文档:(见格式正规的XML:语法 属性 实体 处理指令 样式单 CDATA节). ...
- TypeScript 学习四 面向对象的特性,泛型,接口,模块,类型定义文件*.d.ts
1,面向对象的特性一:类,继承,见上一篇博客: 2,面向对象的特性二: 泛型(generic):参数化的类型,一般用来限制集合的内容:指定只能放某个类型的元素 如下图中的尖括号中的Person,就代表 ...
随机推荐
- Python 小而美的函数
python提供了一些有趣且实用的函数,如any all zip,这些函数能够大幅简化我们得代码,可以更优雅的处理可迭代的对象,同时使用的时候也得注意一些情况 any any(iterable) ...
- Python 爬虫模拟登陆知乎
在之前写过一篇使用python爬虫爬取电影天堂资源的博客,重点是如何解析页面和提高爬虫的效率.由于电影天堂上的资源获取权限是所有人都一样的,所以不需要进行登录验证操作,写完那篇文章后又花了些时间研究了 ...
- Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境
首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...
- junit4进行单元测试
一.前言 提供服务的时候,为了保证服务的正确性,有时候需要编写测试类验证其正确性和可用性.以前的做法都是自己简单写一个控制层,然后在控制层里调用服务并测试,这样做虽然能够达到测试的目的,但是太不专业了 ...
- Hawk 6. 高级话题:子流程系统
子流程的定义 当流程设计的越来越复杂,越来越长时,就难以进行管理了.因此,采用模块化的设计才会更加合理.本节我们介绍子流程的原理和使用. 所谓子流程,就是能先构造出一个流程,然后被其他流程调用.被调用 ...
- UWP开发必备以及常用知识点总结
一直在学UWP,一直在写Code,自己到达了什么水平?还有多少东西需要学习才能独挡一面?我想对刚接触UWP的开发者都有这种困惑,偶尔停下来总结分析一下还是很有收获的! 以下内容是自己开发中经常遇到的一 ...
- 使用Xamarin开发iOS7应用时隐藏StatusBar方法
在iOS7之前如果需要隐藏StatusBar,比较简单,直接在AppDelegate.cs中使用如下代码就可以进行隐藏: UIApplication.SharedApplication.StatusB ...
- On cloud, be cloud native
本来不想起一个英文名,但是想来想去都没能想出一个简洁地表述该意思的中文释义,所以就用了一个英文名称,望见谅. Cloud Native是一个刚刚由VMware所提出一年左右的名词.其表示在设计并实现一 ...
- Web前端温故知新-CSS基础
一.CSS定义与编写CSS 1.1 CSS的定义 全名:Cascading Style Sheets -> 层叠样式表 定义:CSS成为层叠样式表,它主要用于设置HTML页面中的文本内容(字体. ...
- 2000条你应知的WPF小姿势 基础篇<1-7>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师,对C#和WPF有着极深的热情.最为出色的是他维护了两个博客:2,000Things You Should Know ...