跳到主要内容
版本:master

import

1. 概述

本章主要介绍 GS 语言中的 import 机制,import用于引入外部脚本或插件模块。import 本质上是一种语法糖,简化了模块路径的书写和调用过程,让开发者能够轻松的使用其他脚本文件中定义的功能。

本文档面向GS初学者或需要简单回顾GS的import机制、import别名、export功能的同学。

通过阅读学习本章节,您将理解import的实用方法和工作原理,掌握import别名的是同方法和冲突处理、学会如何导入和使用各种功能插件、了解export功能,实现import的再导出、掌握import路径解析的各种规则和技巧。

2. import 概念

import 可以理解为 GS 语言的"模块引入器"。就像在现实生活中,当你想使用某个工具时,不需要自己重新制造,而是直接从工具箱里拿出来用一样。import 允许你将其他脚本文件中定义的功能"拿过来"在当前脚本中使用。它本质上是一种语法简化手段,让代码更加清晰易读。

与直接写文件路径相比,import 提供了更好的可维护性和代码组织能力。

3. import 基础

3.1 import 声明

import 用于引入一个新的模块或者插件,其本质是一种语法糖。其基本语法如下:

import module_name;
  • 导入指定模块,模块名对应文件路径(省略 .gs 后缀)
  • 导入后可通过 module_name.function_name() 调用模块中的函数
  • module_name 既可以是字符串字面量形式的路径也可以是以.分割的路径名表示的路径(可以省略脚本文件的 .gs 后缀)

import 导入文件后,脚本中的文件名会被替换为相应的文件路径字符串字面量。如下代码所示,下面的两个代码是基本等价的(细节上略有差别,假定example.gs放在指定的根目录下):

import example;
example.func();

基本等价于

"/example.gs".func(); 

import 在导入时,会按照一定规则查找到对应的文件路径,并得到一个等价于字符串常量(内容为导入文件的路径,以'/'开头)的标识符。也正因为此,如下的代码是可以工作的:

import example;

//...
object obj_1 = new_object(example, this_domain());
// example 是一个字符串常量,其内容为"/example.gs"

//...

调用模块中的函数时,相应文件的object会被静态的加载进来,示例如下:

// example.gs
void create()
{
printf("example Instance.\n");
}

public string func()
{
return "Helloworld";
}

// src.gs
import example;
printf("Now test: %s\n" ,example);
printf("%s\n", example.func());
// 此处等效于
// printf("%s\n", load_static(example, this_domain()).func());
printf("%s\n", example.func());

示例3-1:import 示例

运行src.gs得到:

Now test: /example.gs
example Instance.
Helloworld
Helloworld

总而言之,利用import,可以在另外的脚本中封装一些操作,将之整体import进来,然后调用其中的方法。

3.2 import 路径

接下来让我们了解下import路径处理,了解下如何导入当前文件夹脚本、相对根路径脚本、上级路径脚本。假设一个GS项目的根目录下有如下文件组织形式:

root -+-- src -+-- test.gs
| |
| +-- xxx.gs
|
+-- yyy.gs

其中各个文件的内容如下,注意为多文件示例,需根据注释拆分文件:

//yyy.gs
public void hello_gs()
{
writeln("Hello gs");
}

//xxx.gs
public void hello_world()
{
writeln("Hello world");
}

// test.gs
import .xxx;
// import src.xxx;
// import "/src/xxx";
// import "./xxx.gs";
// import src.*;

import yyy;
// import ..yyy;
// import "/yyy.gs";
// import "./../yyy.gs";

xxx.hello_world();
yyy.hello_gs();

示例 3-2:improt 路径示例

可以看到 test.gs 文件中有多种注释掉的文件导入方式,其中所有导入方式都是有效的,具体的导入解释如下:

  • 导入当前脚本文件同文件夹下的文件,以下示例为尝试导入与当前 test.gs 文件同文件夹下的xxx.gs 文件:
    • import .xxx;
    • import "./xxx.gs";
  • 导入相对根路径的文件,以下示例为尝试导入相对根路径的 ->src->xxx.gs 文件:
    • import src.xxx;
    • import "src/xxx.gs";
    • import "/src/xxx";
  • 导入当前脚本文件上级路径的文件,以下示例为尝试导入当前 test.gs 文件上级路径的 yyy.gs 文件:
    • import ..yyy;
    • import "./../yyy.gs";
  • 导入某一路径下的所有脚本文件,以下示例为尝试导入相对根路径的 src 文件夹下的所有脚本文件:
    • import src.*;

3.3 import 别名

import import_name 在导入模块时,可在其后添加 as alias_name,其将 alias_name 视为 import_name 的别名。除部分别名冲突的情况下,两者的使用是等效的。如下代码所示(假定example.gs放在指定的根目录下):

import example as example_alias;// example_alias 作为 example的别名
example.func();
example_alias.func(); //等效于 example.func()

示例 3-3:import 别名示例

而当alias_name与已有变量、import_name、关键字、其他别名冲突时,其表现如下:

  • 与已有变量名冲突时,该冲突别名在变量的作用域内被视为变量,作用域外则可正常作为别名使用。
import example as example_alias;// example_alias 作为 example的别名
if(true)
{
int example_alias = 0;
example.func();
example_alias = 1; //在变量作用域内example_alias为int变量
}
example.func();
example_alias.func(); //在变量作用域外example_alias正常作为别名

示例 3-4:import 别名与已有变量名冲突

  • 与已有的 import_name 冲突,使用该冲突名称时,报错"Ambiguous import name ..."
import example_alias;
import example as example_alias;// example_alias作为别名与已导入的模块名冲突
example.func();
//example_alias.func(); 使用 example_alias 时会报错"Ambiguous import name ..."

示例 3-5:import 别名与其他 import_name 冲突

  • 与已有关键字冲突时,关键字的优先级更高,此时别名无法作为 import_name 使用。
import example as while;
import example1 as if;

// while.func(); while 无法作为 import_name 使用
// if.func(); if 无法作为 import_name 使用

示例 3-6:import 别名与已有关键字冲突

  • 与已有别名冲突,使用该冲突名称时会导致"Ambiguous import name..."错误。
import example0 as example_alias;
import example1 as example_alias;

//example_alias.func(); 使用 example_alias 时会报错"Ambiguous import name

示例 3-7:import 别名与已其他别名冲突

3.4 使用import导入插件

新版 gs(0.1.220125.01)将以下插件(plugins)从 GS 内部拆离,在使用时要求以 improt gs.bin.plugin_name;的方式导入相应 plugin:

archivedebuggeedpjson
languagemsgpacknetworkprofiler
regexunsafeutilsxml

新版GS在使用这些插件中的函数时,需要import对应插件,其示例如下:

import gs.bin.regex;  // import gs.bin.PLUGINS_NAME;
regex.match("abc", "abc");

示例3-8:import导入插件示例

如果需要兼容旧版的 GS 代码,可以使用以下命令行参数(不推荐):

--require PLUGINS_1;PLUGINS_2

导入指定的插件(注意分号在shell内的含义),或者使用(不推荐):

--require all

导入全部内置插件

3.5 导出脚本

一些情况下,希望将一个脚本的import项导出出去,让别的脚本只需import一个脚本就能同时引入其他该脚本的import项。该需求可通过使用export关键字修饰import导入实现,以脚本a.gs为例,其基本语法如下(请自行组织脚本文件尝试):

a.gs
export import b;
export import c;
import d;

a.gs
export 
{
import b;
import c;
}
import d;

示例7-1:export 示例

如上示例中,若有其他文件import a;导入a.gs脚本,则其也会同时导入b.gsc.gs,因为import d;没有标注export所以其不会被文件导入。在一些较旧的GS版本中,没有export关键字,类似的行为可以通过 #pragma export_imports 来实现:

a.gs
#pragma export_imports

import b;
import c;
import d;

示例7-2:旧版本 GS export 示例

如上所示,其他脚本import a之后,连带着也会将b、c、d导入进来。需要注意:#pragma export_imports 和手动标注的export是不可以同时使用的。同时使用会在编译时报告错误。

4. 拓展阅读

​ 更多import导入的相关的内容请参照手册,链接如下:

5. 总结

​ 本章学习了GS语言中的 import 机制,掌握了模块导入的基本语法、别名处理、插件导入方法以及路径解析规则。import 是GS语言模块化编程的基石,能够有效组织大型项目的代码结构。在掌握了GS语言中基础的代码组织方式 improt 后,让我们将学习GS代码组件机制——component。