组合声明
语法
- component 脚本路径1 ;
注释:
-
脚本路径的语法:
- "字符串字面量"
- .? 标识符 (. 标识符)*
描述
在 GS 中,一个脚本对应一个程序,亦可以从脚本实例化产生 对象 实例。
component 声明用于组合其他脚本(使之成为组件),使得这些脚本中定义的成员变量和函数成为当前脚本对应的对象实例的一部分。
在脚本中,可以直接调用组件的 protected 和 public 成员函数:
// a.gs
string member_var = "Hello";
protected void foo()
{
// ...
}
public void bar()
{
// ...
}
private void baz()
{
// ...
}
// b.gs
component .a;
void main()
{
// member_var; // 错误,不能访问组件的成员变量
foo(); // 正确,能访问组件的 protected 成员函数
bar(); // 正确,能访问组件的 public 成员函数
// baz(); // 错误,不能访问组件的 private 成员函数
}
脚本以及其所有组件(以及组件的组件)中的 public 函数可以被从外部调用:
// a.gs
public void foo()
{
// ...
}
protected void bar()
{
// ...
}
// b.gs
component .a;
void baz()
{
foo(); // Ok
bar(); // Ok, 允许调用组件的 protected 成员函数
// this.bar(); // 错误,不能通过 `外部` 访问组件的 protected 成员函数
}
// c.gs
import .b;
object ob = new_object(b);
ob.foo(); // Ok,foo 被集成到了脚本 b 中
// ob.bar(); // 错误,不能访问组件的 protected 成员函数
脚本中可以定义 虚函数。允许在脚本中覆写组件脚本中定义的虚函数:
// a.gs
public virtual void foo()
{
write("a.foo\n");
}
// b.gs
component .a;
public override void a.foo()
{
write("b.foo\n");
}
调用虚函数时,调用的是最终覆写的函数实现。
路径
在组合声明中,支持两种形式的脚本路径:
-
以
.分割的路径:如果以.开头,表示是相对路径,否则是绝对路径;去掉开头的.之后,将剩余的.替换为/, 如果是相对路径,将当前脚本所在的路径拼接在前面;如果是绝对路径,将/拼接在 前面;然后在得到的结果末尾拼接.gs:a.b.c→/a/b/c.gs.e.f→/<当前脚本所在路径>/e/f.gs -
字符串字面量:表示脚本的完整路径,支持相对路径和绝对路径两种形式,如果以
/开头,表示绝对路径,否则应当以./或../开头,表示相对路径,相对路径是相对于当前脚本所在路径的:"./a/b/c.gs"→/<当前脚本所在路径>/a/b/c.gs"../e/f.gs"→/<当前脚本所在路径>/../e/f.gs"/e/f/g.gs"→/e/f/g.gs
对于任意翻译得到的路径,都应当指向:
- 启动 GS 时,使用命令行参数
/r指示的运行目录 之下 的某个位置, - 使用
mount机制挂载的某个目录之下的某个位置 - 一个
file://指定的绝对文件路径
在组合时,根据最终得到的路径(此处假设是 /a/b/c.gs),会查找以下位置的脚本文件:
/a/b/c.o/a/b/c.gs/a/b/c/c.o/a/b/c/c.gs
如果没有找到任何一个文件,编译期应当报错。
路径常量
在组合声明语句中,会在脚本中引入一个与组件文件同名的局部常量;该常量的值为组件脚本的路径:
// /example/a.gs
component b;
component .b2;
void main()
{
write(b); // 输出组件 b 的路径:"/b.gs"
write(b2); // 输出组件 b2 的路径:"/example/b2.gs"
}
main();
如果导入的组件中包含同名的组件,因为此时常量名冲突,使用此常量将导致编译期错误:
component a;
component another.a;
// writeln(a); // 错误:a 有歧义
组合和构造顺序
构建对象实例时,会依照如下顺序调用各个组件的 ::entry(初始化成员变量)和 create(构造函数):
- 从上到下,先构建 组件,再构建自身;递归执行此规则。
- 对于已经被执行到的组件,跳过。
- 先执行所有的
::entry,之后再执行create
在对象被关闭时,按照上述顺序的逆序执行 destruct(析构函数)。