# import 语句

aardio 设计了一套轻便灵活的库、命名空间体系，这使 aardio 程序能更好地支持的模块化编程。

请参考：[命名空间](../language-reference/namespace.md)

## 导入库

import 语句将一个外部命名空间导入到当前命名空间。

> import 语句只能用于导入库创建的命名空间或类命名空间（定义类时会创建同名的命名空间），不能用于导入单个库内部的成员，不能也不必要用于导入函数对象。例如我们可以用 import 语句导入 fsys 库或 fsys.file 类库，但不能单独导入 fsys.copy 函数。注意 io.file 类对象由 io 内置库提供，不允许也不需要单独导入 io.file 。

import 语句首先会在当前命名空间下查找同名对象是否存在，查找时不会触发命名空间的元方法，如果同名对象已经存在并且不是指向相同的库 - 则会报错。import保证不会在同一线程重复加载相同的库。外部库内的代码在一个线程中仅运行一次。  
  
例如：

`import a.b.c //将外部库a.b.c导入当前命名空间`

import 语句内使用的库命名空间第一个字符不能为下划线 `_`。  
aardio 不建议在库命名空间中使用任何下划线( `_.aardio` 表示默认库除外 )。如果在 IDE 中右键创建库或改动库名，aardio 将会自动移除下划线。如果你确实要这样做，请在资源管理器中手动改名。

aardio的库有三种：

*   内置库(Builtin Libraries)  
    内置库由 aardio 内核提供，内置库列表如下：
    - raw 原生接口开发与原生类型操作
    - string 字符串函数库
    - table 表与数组函数库
    - math 数学函数库
    - io 文件与标准输入输出函数库
    - time 日期时间函数库
    - thread 多线程函数库
    - fiber 纤程函数库 
    - com COM 接口函数库
    - builtin 这个库不会被直接使用，但是我们可以在 builtin 库中查看一些内置函数的源代码。

    内置库里只有 com 库需要调用 import 语句才能真正导入并可用。
    win.ui 库已经自动导入了 com 库。

    其他内置库不需要用 import 语句导入，
    内置库默认加载到全局命名空间，在非全局命名空间内访问内置库时应当在库名称前面添加 `..` 前缀，通过操作符 `..` 可在自定义命名空间内部直接访问外部全局命名空间的成员。
 
*   公共库(Common libraries)  

    公共库是指自 aardio 开发环境根目录 `~\lib` 加载的库。
    所有 aardio 工程都可以使用公共库。

    公共库分为两个部分：
    - 标准库(Standard Libraries)  
    标准库是由 aardio 开发环境自带的官方标准库。 
    - 扩展库(Extension Libraries)   
    扩展库需要安装才能使用，扩展库通常会安装到公共库目录。
    实际上所谓安装基本上就是下载复制粘贴，aardio 的扩展库安装极其简单。

    > 公共库在开发环境中都会显示在『标准库』面板内，
    > 如果安装了其他扩展库，也可以在标准库面板内查看，但这些需要额外安装的扩展库并不属于标准库，仅仅是存储在相同目录并显示在相同的视图。
       
*   用户库(User Libraries)  
    用户库是用户在aardio应用程序根目录下的 `\lib`  目录下建立的库。库路径与命名空间路径完全一致。  
    如果是程序入口是打开工程内的代码文件，根目录为工程目录，否则为启动aardio文件所在目录。  
    
  
在发布exe文件时，编译器将会自动分析程序中引用到的库并加入目标程序中。并丢弃未引用的库。

1. 如果希望引用一个库，但发布EXE时希望排除该库（不添加到目标程序)，请使用 `global.import()` 导入库 - 以告知发布程序你希望引用一个库，但是不希望添加到目标程序。 例如调用  `global.import("fsys")` 可导入 fsys 库。 
  
2. 如果应用程序不希望引用某个库，但是发布后的 EXE 可能在将来通过动态加载 aardio 代码引用该库，这时候可以将 import 语句移到注释中 - 告知发布程序你不希望引用某个库，但是希望添加到目标程序中。  
  
3. 在开发环境中调试运行时，aardio 为了调试与优化的需要可能会加载一些你编写的代码中并没有引用的库，例如标准库 win.ui 默认会加载 win.ui.ctrl 命名空间下的一些基础控件库，而这些库又会引用一些基它的标准库。aardio 为了优化启动速度，只有在发布生成EXE时才会完全排除未引用的库。 你完全可以像其他开发工具一样，每次都先生成 EXE 后再运行，当然这样就会像其他一些语言一样每次都要等很久。 

## 详解 import 导入库的过程

import 导入库时查找顺序为:

1. 内置库

2. 公共库

3. 用户库

在开发时，公共库与用户库都是外部库。   
外部库文件的物理路径与命名空间路径保持一致(将物理路径中的斜杠替换为圆点就是命名空间路径)。

我们以下面的 import 语句为例详解导入库的过程：

```aardio
import time.ole //将外部库 time.ole 导入当前命名空间
```

1.  查询库缓存

    已导入的库会直接加载到当前命名空间，
    如果库缓存已经有这个库则直接导入，不会重复加载相同的库。
    如果库缓存里找不到，就继续下面的查找过程。  
  
2. 查询内置库

    如果内置库中存在该库，导入库，否则继续下面的过程。 

    发布为 EXE 以后所有开发时可以正常引用的外部库都会被编译为内置库，同样拥有最高的加载优先级。  

3. 查询公共库

    如果仍然没有找到库。
    
    在启动当前进程的 EXE 文件根目录下的 `~\lib` 目录下查询公共库文件，从根目录逐渐向下层子目录开始搜索，查找顺序如下(按查找顺序先后排列)：  

    
    ```aardio
    ~\lib\time.ole.aardio
    ~\lib\time\ole.aardio
    ~\lib\time\ole\_.aardio 
    ```  

    如果找到 aardio 代码文件或编译后的二进制 aardio 文件，加载并运行，并将文件路径作为参数传递给目标文件。

    > 注意：公共库不但包含标准库，还包含其他放在公共库目录的扩展库，所有公共库遵守相同的加载规则。

4.  查询用户库

    如果仍然没有找到，按第三步的过程，在应用程序根目录 `\lib` 下查找用户库, 查找顺序如下(按查找顺序先后排列)：  

    
    ```aardio
    \lib\time.ole.aardio
    \lib\time\ole.aardio
    \lib\time\ole\_.aardio  
    ```  

    如果找到aardio代码文件或编译后的二进制aardio文件，加载并运行，并将文件路径作为参数传递给目标文件。

    应用程序根目录一般是指当前工程目录的 `\lib` 目录，或独立启动的 aardio 文件所在目录下的 `\lib`目录。

    请参考： [aardio 应用程序根目录](./builtin/io/path.md#app-path)

5. 查找扩展库 

    如果当前是运行在aardio开发环境中，仍然没有找到匹配的库文件，aardio会检查扩展库列表并安装扩展库。 

    aardio 安装扩展库只是简单地复制粘贴，
    如果要卸载，只要右键跳转到定义，直接删除库文件即可。

6. 检查库是否加载成功

    最后 aardio 检查目标文件是否成功运行，  
    目标文件是否创建了新的命名空间全局命名空间 `..time.ole`。  
    如果导入命名空间成功，则导入当前命名空间( self 命名空间 )。

    在 aardio 中库一定要自行创建与路径相同的命名空间。
    如果未创建一致的命名空间，则导入库失败。

## import global 语句 <a id="import-global" href="#import-global">&#x23;</a>


注意在 aardio 中 `import global` 是一个完全独立的语句，其作用并不是导入库。

注意：

- global 只是全局命名空间，并不是一个库模块。
- `import global` 的作用并不是将 global 作为库导入，实现上 `import global` 不会执行任何导入库的操作。

作用：

`import global` 的作用是在非全局命名空间允许直接访问全局命名空间成员。

举例，原来我们自定义一个命名空间，需要通过 `..print` 访问 `global.print` 这样的全局命名空间的对象。示例：

```aardio
namespace myNameSpace{
	..print("默认访问全局命名空间对象要在前面加 .. 操作符")	
}
```

这是 aardio 与其他编程语言一个较大的区别，  
这看起来有些麻烦，但有较大的好处：

- 各命名空间完全独立模块化，代码清晰易维护，全局名字与当前命名空间的命名即使重名也互不干扰。
- 访问变量的速度更快，看起来多打了两点，其实在执行更简洁更快。

需要密集使用的对象，还可以声明为局部变量，例如：

```aardio
namespace myNameSpace{
	
	var print = ..print;
	
	for(i=1;10;1){
		print(i);
	}
}
```

如果我们希望在自定义命名空间里可以直接访问全局对象，  
只要加上 import global 就可以省略 `..` 了。

示例：

```aardio
namespace myNameSpace {
	
	import global;
	  
	for(i=1;10;1){
        
		//使用任何全局对象都不用打点了
		print(i);
	}
}
```

这样做仍然会优先访问当前命名空间的对象，  
只有当前命名空间找不到的名字就会自 `global` 全局命名空间获取。

`import global` 会有非常轻微的效率代价，对于普通的应用层代码可以忽略也感觉不出来。  
但如果是编写库模块、底层代码、编写需要密集执行的代码、对效率或代码质量要求高，那么不建议使用 `import global` 。有时候哪怕是非常小的代价，累积起来也是可观的。

### 原理说明：

`import global` 会将当前命名空间的 _get 元方法指向 global 全局表，这会导致在当前命名空间中访问不存在的成员时转向访问全局表。

- 如果命名空间的 _get 元方法已经指向 global 则忽略不操作。
- 如果命名空间尚未添加元表则先为命名空间新建并设置一个元表，如果尚未指定 _get 元方法，则将其指向 global 对象。
- 如果命名空间的 _get 元方法已指向 global 以外的对象则抛出异常。