模 块 2.014

2019-04-13 14:47发布

模块:
模块声明 多个声明定义
多个声明定义

多个声明定义:
单个声明定义
单个声明定义 多个声明定义

单个声明定义:
属性指示符
导入声明
枚举声明
类声明
接口声明
聚集声明
单个声明
构造函数
析构函数
不变量
单元测试
静态构造函数
静态析构函数
Debug规范
Version规范
Mixin声明
;

模块同源文件是一一对应的。模块名就是去掉路径和扩展名的文件名。

模块自动为它的内容提供一个名字空间。模块跟类有一点相像,不同之处是:

[color=red]• 每个模块只有一个实例,并且它是静态分配的。[/color]
• 模块没有虚函数表。
[color=red]• 模块不能继承,它们没有父模块,等等。
• 每个文件只有一个模块。[/color]• 模块的符号可以导入。
• 模块总是[color=red]在全局作用域内编译[/color],并且不受周围的特征或其它修饰符影响。

多个模块可以组织成一个结构,叫做“包(packages)”。

模块提供了以下几种担保:

• 模块导入的顺序并不会对语义产生什么影响。
• 一个模块的语义不会被那些导入它的模块所影响。
• 如果一个模块 C 导入了模块 A 和 B,那么任何对 B 的修改都不会隐式地更改在模
块 C 里的跟 A 相独立的代码。


2.1 模块声明

“模块声明”指定了模块的名称和它所属的包。如果不指定,模块名将设定为去掉路径和扩
展名的文件名。

模块声明:
module 模块名 ;

模块名:
标识符
模块名 .标识符

最右面的“标识符”是模块所在的“包”。包在源文件路径中对应于目录名。
如果出现的话,“模块声明”按照语法位于源文件的开头,并且每个源文件只能有一个。
样例:
module c.stdio; // 这是模块 stdio,它位于 c 包中

按照惯例,包和模块名都为 小写。这是因为 包 和 模块名称 同操作系统中的目录名和文件名 一一对应,而许多文件系统不区分大小写。把所有的包和模块名称小写将减少在不同文件系统之间 迁移 项目时的问题。


2 导入声明

与文本的包含文件不同,D 通过使用“导入声明”直接导入符号:

导入声明:
import 导入列表 ;
static import 导入列表 ;

导入列表:
导入
导入绑定
导入 , 导入列表

导入:
模块名
模块别名标识符 = 模块名

导入绑定:
导入 :导入绑定列表

导入绑定列表:
导入绑定
导入绑定 , 导入绑定列表

导入绑定:
标识符
标识符 = 标识符

[color=red]ModuleAliasIdentifier:
Identifier 2.014[/color]

有好几种形式的“导入声明”,从一般的到有细密纹理的(fine-grained)导入。

在“导入声明”里的声明顺序并不重要。

模块名(位于“导入声明”中)必须使用它们所处的那个包的名称来完整修饰。 它们就不
会被认为是相对于那个导入它们的模块。


3 基本导入

最简单的导入形式就是只做列出要被导入的模块:

import std.stdio; // 导入模块 stdio(自 std 包里)
import foo, bar; // 导入杠杆?? foo 和 bar
void main()
{
writefln("hello! "); // 调用 std.stdio.writefln
}

[color=red]基本导入工作原理[/color] 就是:首先,在当前名字空间里搜索名字。如果没有找到,那么就到所有导入的模块里去查找。如果在这些导入模块中唯一找到一个,则使用它。
如果在多个导入模块里找到,则就出现错误。

module A;
void foo();
void bar();

module B;
void foo();
void bar();

module C;
import A;
void foo();
void test()
{ foo(); // C.foo() 被调用,它在搜索导入模块之前被找到
bar(); // A.bar() 被调用,因为搜索了导入模块
}

module D;
import A;
import B;
void test()
{ foo(); // [color=red]错误,是 A.foo() 还是 B.foo() 呢?[/color]
A.foo(); // 正确,调用 A.foo()
B.foo(); // 正确,调用 B.foo()
}

module E;
import A;
import B;
alias B.foo foo;
void test()
{ foo(); // 调用 B.foo()
A.foo(); // 调用 A.foo()
B.foo(); // 调用 B.foo()
}


4 公共导入

[color=red]默认情况下,导入是“private(私有的)”。[/color]即表示,如果 A 导入模块 B,同时 B 又导入模块C,则 C 的名字是不会被搜索的。

一个导入可以特别地声明为“public(公共的)”,这时它会处理成这样:对于带有“导入声明”的模块,它的任何导入模块也会导入那些公共导入的模块。

module A;
void foo() { }

module B;
void bar() { }

module C;
import A;
public import B;
...
foo(); // 调用 A.foo()
bar(); // 调用 B.bar()

module D;
import C;
...
foo(); // 错误,foo() 未定义
bar(); // 正确,调用 B.bar()


5 静态导入

基本导入对于相对没有几个模块和导入的程序很有效。如果存在大量模块导入,则在各种不
同的导入模块里,名字 冲突 就会出现。

防止此种情况发生的一种方式就是使用[color=red]“静态导入(static imports)”。[/color]静态导入要求我们使用带 完整修饰 的名称来 引用 模块名。

[color=red]static[/color] import std.stdio;
void main()
{
writefln("hello!"); // 错误,writefln 未定义
std.stdio.writefln("hello!"); // 正确,writefln 被完全限定
}


6 更名的导入

可以为某个导入给出本地名称;通过这个本地名称,所有对该模块符号的引用都必须进行限
定(be qualified with):

import [color=red]io = std.stdio[/color];
void main()
{
io.writefln("hello!"); // 正确,调用 std.stdio.writefln
std.stdio.writefln("hello!"); // [color=red]错误,std 未定义[/color]可能已经覆盖了 std.stdio 名称

writefln("hello!"); // [color=red]错误,writefln 未定义[/color]
}

更名的导入在处理很长的导入名时很方便。


7 选择性导入

特定的符号可以 特别 地从一个模块被导入,并被绑定到当前名字空间里。

import std.stdio[color=red] : [/color]writefln, foo = writef;
void main()
{
std.stdio.writefln("hello!"); // 错误,std 未定义

writefln("hello!"); // 正确,writefln 被绑定到当前名字空间

writef("world"); // 错误,writefln 未定义

foo("world"); // 正确,调用 std.stdio.writef()

fwritefln(stdout, "abc"); // 错误,writefln 未定义
}

[color=red]static 不能跟选择性导入一起使用。[/color]


8 更名 的且带 选择性 的导入

更名的且带选择性的导入被组合的情形:

import io = std.stdio : foo = writefln;
void main()
{
writefln("bar"); // 错误,writefln 未定义

std.stdio.foo("bar"); //[color=red] 错误,foo 被绑定到当前名字空间[/color]

std.stdio.writefln("bar"); // 错误,std 未定义

foo("bar"); // 错误,foo 被绑定到当前名字空间,
// FQN 不需要

io.writefln("bar"); // 正确,io=std.stdio 将在当前名字空间里的名称 io
// 绑定来引用整个模块

io.foo("bar"); // 错误,foo 被绑定到当前名字空间,
// 而 foo 不是 io 的成员


9 模块作用域运算符

有些时候,有必要重写通常的词法作用域规则以访问被局部名称 掩盖 的名称。可以用 全局作用域 运算符‘.’ 达到这个目的,全局运算符位于标志符之前:
int x;
int foo(int x)
{
if (y)
return x; // 返回的是 foo.x,而非全局的 x
else
return [color=red].x; // 返回全局的 x[/color]}

前导的‘.’意味着在 模块作用域 级别 查找名称。


10 静态构造 和 析构

静态构造函数 是用来在 main() 之前 [color=red]运行[/color] 的 初始化模块或类的代码。静态析构函数是在 main() 返回 [color=red]之后[/color] 执行的代码,通常用来 释放 系统资源。

在一个模块里可以有 多个 静态构造函数 和 静态析构函数。静态构造函数的是以词法顺序运行的,而静态析构函数 则是以词法 相反 的顺序运行。

10.1 静态构造 的顺序

静态初始化的顺序 隐式 地由模块内的 import 声明的 顺序 决定。每个模块都会在它所依赖的模块 之后 调用自己的静态构造函数。

除了这条规则以外,模块静态构造函数的执行顺序是不定的。

导入声明中的 循环[color=red](循环依赖)[/color] 是允许的,只要不是两个模块都含有静态构造或析构函数就行。

如果违反这条规则会在运行时产生异常。

10.2 一个模块中 静态构造 的顺序

在模块内部,静态构造 会按照它们出现的 词法 顺序执行。

10.3 静态析构 的顺序

静态析构 将按照与构造函数 相反 的顺序执行。只有当模块的 静态构造函数 成功执行后,才会执行 静态析构函数。

10.4 单元测试的顺序

单元测试是以出现在 模块 里的 词法 顺序运行的。


11 Mixin 声明

Mixin声明:
mixin ( 赋值表达式 ) ;

赋值表达式 必须在编译时求值成一个 [color=red]常量字符串[/color]。该字符串的 文本内容 必须要可编译成一
个 有效 的 多个声明 定义,而且同样地被编译。


。。。