模块:
模块声明 多个声明定义
多个声明定义
多个声明定义:
单个声明定义
单个声明定义 多个声明定义
单个声明定义:
属性指示符
导入声明
枚举声明
类声明
接口声明
聚集声明
单个声明
构造函数
析构函数
不变量
单元测试
静态构造函数
静态析构函数
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]。该字符串的 文本内容 必须要可编译成一
个 有效 的 多个声明 定义,而且同样地被编译。
。。。