# 05 对象模型（class/unit）

本章整理 Object TSL 的类、对象、属性、方法与相关语法。

## 目录

- [Object TSL](#object-tsl)
  - [类和对象](#类和对象)
    - [概述](#概述)
    - [类类型声明的语法](#类类型声明的语法)
    - [类类型声明的位置](#类类型声明的位置)
    - [类的实例化](#类的实例化)
    - [对象成员的访问](#对象成员的访问)
  - [类继承和作用域](#类继承和作用域)
    - [继承](#继承)
    - [作用域](#作用域)
  - [字段](#字段)
    - [字段简介](#字段简介)
    - [字段类型与弱引用](#字段类型与弱引用)
    - [static 静态字段](#static-静态字段)
    - [const 常量成员](#const-常量成员)
  - [方法](#方法)
    - [方法简介](#方法简介)
    - [声明和实现](#声明和实现)
    - [覆盖(override)](#覆盖override)
    - [多态](#多态)
    - [隐藏](#隐藏)
    - [重载(overload)](#重载overload)
    - [构造函数](#构造函数)
    - [析构函数](#析构函数)
    - [指定类](#指定类)
    - [Self](#self)
    - [类方法](#类方法)
  - [属性 Property](#属性-property)
  - [索引器(index)](#索引器index)
  - [TSL 对象的创建](#tsl-对象的创建)
    - [New](#new)
    - [CreateObject](#createobject)
  - [类信息](#类信息)
    - [FindClass](#findclass)
    - [ClassInfo](#classinfo)
    - [objectstate](#objectstate)
    - [tslassigning](#tslassigning)
    - [对象的引用计数](#对象的引用计数)
  - [IS 关键字](#is-关键字)
  - [函数信息](#函数信息)
    - [FindOverLoad](#findoverload)
    - [TSLObjects](#tslobjects)
    - [FindFunction](#findfunction)
    - [ThisFunction](#thisfunction)
    - [FunctionInfo](#functioninfo)
  - [算符重载](#算符重载)
    - [Operator](#operator)
    - [算符重载的案例](#算符重载的案例)
    - [面向对象的 Operator 重载支持[]](#面向对象的-operator-重载支持)
    - [面向对象的 Operator 算符重载支持++,--,+=,-=](#面向对象的-operator-算符重载支持)
  - [单元中的类](#单元中的类)
    - [继承单元中的类](#继承单元中的类)
    - [构造单元中的类实例](#构造单元中的类实例)
    - [预定义：ParentClassInUnit](#预定义parentclassinunit)
    - [单元中的类-应用实例](#单元中的类-应用实例)
  - [TSL 内置对象使用大全](#tsl-内置对象使用大全)
    - [TSL 内置对象使用大全简介](#tsl-内置对象使用大全简介)
    - [FTP 对象](#ftp-对象)
    - [TRSA](#trsa)
    - [TCipher](#tcipher)
    - [TStringList 对象](#tstringlist-对象)
    - [THashedStringList 对象](#thashedstringlist-对象)
    - [TStream 对象](#tstream-对象)
    - [TMemoryStream 对象](#tmemorystream-对象)
    - [THandleStream 对象](#thandlestream-对象)
    - [TFileStream 对象](#tfilestream-对象)
    - [TIniFile 对象](#tinifile-对象)
    - [TMemIniFile 对象](#tmeminifile-对象)
    - [TRegistryIniFile 对象](#tregistryinifile-对象)
    - [TWebResponse 对象](#twebresponse-对象)
    - [TWebRequest 对象](#twebrequest-对象)
    - [TCookieCollection 对象](#tcookiecollection-对象)
    - [TCookie 对象](#tcookie-对象)
    - [TSessionMan 对象](#tsessionman-对象)
    - [TSession 对象](#tsession-对象)
    - [SMTP 对象](#smtp-对象)
    - [Pop3 对象](#pop3-对象)
    - [MailMsg 对象](#mailmsg-对象)
    - [MessagePart 对象](#messagepart-对象)

## Object TSL

### 类和对象

面向对象以类组织数据与行为，类的实例称为对象。类通过封装、继承与多态实现复用与扩展。

#### 概述

类（或者类类型）定义了一个结构，抽象地，这个结构既可以包括数据，也可以包括行为。具体地，类可以包括字段、方法和属性。

类的字段、方法和属性被称为它的成员，类的实例叫做对象。

- 字段：对象的数据项。
- 方法：与类关联的函数，通常作用于对象实例。
- 属性：字段的访问接口，可用方法实现读写逻辑。

声明了类以后，程序员可以创建作为此类的实例对象。尽管有时类和对象叫法可互换，但它们是不同的概念。类定义对象的类型，但它不是对象本身。对象是基于类的具体实体，有时称为类的实例。

```tsl
type MyClass = class
end;
```

上面代码声明了一个类类型

```tsl
obj := CreateObject("MyClass");
```

上面代码创建了类 MyClass 的实例.

创建类的实例后，将向程序员传递回对该对象的引用。

在前面的示例中，obj 是对基于 MyClass 的对象的引用。此引用指向了新对象，但不包含对象数据本身。

#### 类类型声明的语法

```tsl
type
    ClassName := class([BaseClass1[, BaseClass2, ...]])
    private
        memberList
    public
        memberList
        function Create(); overload; // 构造函数（可省）
        function Destroy(); // 析构函数（可省）
        function Func1(); // 成员方法
    end;
```

说明：

type 是一个关键字，说明要声明一个类型，class 也是一个关键字，表示要声明一个类类型。

className 是要声明的你自己定义的类类型的名字，可以是任何有效标志符.

BaseClass1,BaseClass2,… 可选，表示要继承的父类型，可以是 0 个、1 个或多个,如果是多个其中用逗号分割.如果此参数为空，可以省略 class 后面的小括号

memberList 声明类的各成员，也就是它的字段、方法和属性。可选.

注：

一个 Type …End;只能声明一个类类型；

说明：即使 memberList 为空也不能省略 `end;`。
完整示例：

```tsl
type Person = class
    // 字段
    name;
    // 方法
    function SetName(new_name);
    begin
        name := new_name;
    end;
    function DisplayName();
    begin
        WriteLn(name);
    end;
    // 属性
    property TheName read name write name;
end;
```

以上代码包括了一个完整类类型的声明：

声明了一个 Person 的类类型，也为 Person 类型声明了下面的成员：

字段：name

方法：SetName 和 DisplayName

属性：TheName

#### 类类型声明的位置

与其他类型不同，类类型必须在实例化之前声明并给定一个名称。有三种地方可以声明类类型：

1、在程序（program）的最外层声明类，在执行语句块的前面，而不能在过程或函数中声明.

示例：

```tsl
program Test;
// 声明了一个名称为 MyClass 的类类型
type MyClass = class
    // 这里可以定义类的成员：字段、方法、属性
end;
begin
    // 这里可以初始化并使用上面声明的类类型
    obj := CreateObject("MyClass");
end.
```

2、在主函数的后便声明，示例：

```tsl
function abcd();
begin
    A := CreateObject("myClass");
end;
type myClass = class
end;
```

3、在非 Program 开头的 TSL 语句段后。

如：

```tsl
…………..
A := CreateObject("myClass");
…………
type myClass = class
end;
```

4、也可以在 "TSL 解释器安装目录\funcext\" 下面或者在其下的子目录下建立一个同名的.tsf 文件，在文件内部声明一个类类型，文件名和类名必须相同。

如:把 myClass 的类类型放在 myClass.tsf 文件中。这样可以被全局引用。

#### 类的实例化

创建一个类的实例对象,TSL 中提供了两种方式：

方式一：调用 CreateObject 函数进行创建：

```tsl
CreateObject
( <classname:String|ClassType> [, P1, P2…]):TSLObject
```

用类名字符串或者用一个类类型来创建一个类的对象，返回新建对象的引用。

方式二：使用 New 关键字方式进行创建：

```tsl
new ClassName([P1, P2...]): TSLObject
```

和 CreateObject 类似，但 ClassName 不再需要是一个字符串，而是直接写出类名即可，在类名后用()，括号里可以加入构造函数的参数。

注意：类名两端的括号不能省略，可使用字符串常量，也可以使用字符串变量。

也可以使用类类型作为对象的构造，类类型可以用 class(classname)以及 findclass 等来获得。

如果要创建类 Person 的实例，写法可以是以下两种方式：

```tsl
obj := CreateObject("Person");
obj := new Person();
```

如果类的构造器有参数，则需要把参数列表一起传给 CreateObject 函数，如给类 Person 的实例构造时指定两个属性:

```tsl
obj := CreateObject("Person", "zhangfei", 25);
obj := new Person("zhangfei", 25);
```

可以把对象的引用赋值给另外一个变量 Obj2 := Obj1;
这时 Obj2 和 Obj1 指向同一个实例,而不是 2 个实例。

#### 对象成员的访问

成员变量以及方法的访问

操作成员变量以及方法均采用.操作符。

例如:

```tsl
Human := new Person("张三");
Human.mSex := "男";
Human.mBirthday := 20120101T;
Echo Human.GetAge();
```

##### ?.模式访问 NIL 对象

如果某个内容本身可能是 NIL，也可能是对象，如果我们希望当 NIL 的时候访问方法或者成员不报错返回 NIL。

这个时候我们传统需要使用 a?a.b:nil，如果对象访问是嵌套的，例如 a.b.c，那么需要使用 a
and a.b?a.b.c:nil，这种情况下使用?.模式会更为表达清晰。

TSL 支持使用 a?.b?.c 完成上述需求，该功能和 JAVASCRIPT 的?.相仿。同样的，我们也支持 a?.[index]模式访问，当 a 为 NIL 返回 NIL

具体可参考：?.模式

### 类继承和作用域

#### 继承

面向对象中，继承是基础。我们可以对已有的类复用和扩展。通过继承我们可以让新类拥有了已有类的数据和行为。也可以扩展新类，使它具有更多的数据和行为。

我们这里称已有类为新类的基类或父类，有的地方也叫超类。

我们称新类为已有类的子类或派生类。

由于 TSL 支持多级继承。其中基类，基类的基类（如果有的话）都统一叫做子类的祖先类。

子类可以通过继承获得基类的数据和方法，有效复用，提高开发效率，使代码易于维护。

TSL 继承的实现方式是：声明类类型时，在 `class` 后的括号中指定父类名称。TSL 支持多重继承，基类之间用逗号分隔。格式为：

```tsl
type MyClass = class(BaseClass1[, BaseClass2, ...]);
```

以上代码定义了一个叫做 myClass 的类，它继承自 BaseClass1、BaseClass2 列表。这样我们称 MyClass 为子类，BaseClass1，BaseClass2…为 MyClass 的基类或父类。也可以叫祖先类.

子类 MyClass 将获得基类(BaseClass1、BaseClass2、…)的所有非私有数据和行为,此外新类可以为自己定义新的数据和行为进行扩展,也可以重新定义基类中的行为.

示例：

```tsl
type A = class
public
    procedure F1();
end;

procedure A.F1()
begin
    WriteLn("call A.F1");
end;

type B = class(A)
end;

function Demo(): void
begin
    obj := CreateObject("B");
    obj.F1();
end;
```

说明：类 `B` 继承了类 `A` 的方法 `F1`，不需要重复定义。
如果子类的多个基类都有同样的方法，在子类中调用这个方法时需要为这个方法指定具体的父类，方法为在调用的方法前面加上 Class(基类名称).

示例

```tsl
type Base1 = class
function F();
begin
    writeln("Base1.F");
end;
end;
type Base2 = class
function F();
begin
    writeln("Base2.F");
end;
end;
type SubClass = class(Base1, Base2)
function CallBaseF();
begin
    class(Base2).F(); // 指定要调用类Base2的F方法。
end;
end;
begin
    S := CreateObject("SubClass");
    S.CallBaseF();
end.
```

以上代码演示在类内部调用多个基类相同方法的方法，如果在子类的外部调用这个方法，总是调用基类声明在最前面的类的方法。没有办法调用其他基类的方法，一个技巧是必须在子类中包装这个方法，在外面调用包装的方法。

##### 继承单元类以及成员类

```tsl
type AA = class(UNITB.CC)//支持单元名.类名模式继承
....
end;
```

同时也支持子类继承：

```tsl
new AA();
type AA = class(BB.CC)//CC是BB的成员类
FAA;
end;
type BB = class
FBB;
type cc = class
FCC;
function create();
begin
    echo "create";
end;
end;
end;
```

#### 作用域

类的每个成员都有一个称为可见性的属性，我们称为作用域。

作用域决定了一个成员在哪些地方以及如何能被访问。TSL 用下面 3 个关键字之一来表示它：private、protected、public

Private:私有域内定义的成员变量以及方法以及其他均属于私有的，仅供在类成员方法中使用

即表示最小的访问能力，只能在类内部的方法、属性调用，不能从类的外部调用；也不能被继承。

Protected:相比 Private，子类可以引用，但外部不行

即表示中等的访问能力，在声明它的类的模块中是随处可用的，并且在它的派生类中也是可用的。

Public：公有域内定义的内容均可以被内外部调用

即表示最大的访问能力，只要能使用类的地方都是可用的。

注：

若声明一个成员时没有指定其作用域，则它和前面的成员拥有相同的作用域.

若在类声明的开始没有指定作用域,
TSL 成员（包括字段、方法、属性）默认的作用域是 public。

作用域关键字可以在程序中重复出现多次。

为可读性考虑，最好在声明类时用作用域来组织成员：把所有的 private 成员组织在一起，接下来是所有的 protected 成员，最后是 public 成员。用这种方法，每个可见性关键字最多出现一次，并且标明了每个新段的开始。所以，一个典型的类声明应该像下面的形式：

```tsl
type
MyClass :=
class
(BaseClass)
private
... { private declarations here}
protected
... { protected declarations here }
public
... { public declarations here }
end;
;
```

示例：

下面的代码详细说明了继承与作用域的关系

```tsl
program test;
type A = class()
private
function F1();
begin
    Writeln("from private F1");
end;
protected
function F2();
begin
    Writeln("from protected F2");
end;
public
function F3();
begin
    Writeln("from public F3");
end;
function F4();
begin
    Writeln("from public F4");
    F1(); // 正确,可以在类内部的方法调用private 的成员。
    F2(); // 正确,可以在类内部的方法调用protected的成员
    F3(); // 正确,可以在类内部的方法调用public的成员
end;
end;
type B = class(A)
function F5();
begin
    Writeln(" Inherit test :call private funciton ");
    F1(); // 错误,不能调用基类的私有方法
end;
function F6();
begin
    Writeln("inherit test: call protected function");
    F2(); // 正确,调用基类的protected 方法
end;
function F7();
begin
    Writeln("inhert test :call public function ");
    F3(); // 正确,调用基类的public 方法
end;
end;
begin
    AA := createobject("A");
    AA.F1(); // 错误,不能调用类型的private方法
    AA.F2(); // 错误,不能调用类型的protected方法
    AA.F3(); // 正确,可以调用类型的public 方法
    AA.F4(); // 正确,可以调用类型的public 方法,方法内部可以调用private成员.
end.
```

### 字段

#### 字段简介

字段就像属于对象的一个变量，我们也称之为成员变量，它可以是任何类型。对象用户存放对象的数据。

给类定义字段非常简单,只要把字段的名字在类的声明中列出即可。定义方法：

```tsl
type myClass = class
Field1;
Field2;
end;
```

以上的代码声明了一个 myClass 的类，同时定义了 2 个字段，Field1 和 Field2

引用：

1、在类内部的方法直接调用字段名

2、外部调用：实例对象.字段名，例如

```tsl
myObj := CreateObject("myClass");
myObj.Field1 := "This is a Field";
```

注：

可以为字段指定作用域

#### 字段类型与弱引用

字段可以使用 `:` 指定类型。类型注解不参与编译检查，可任意命名，仅用于说明与阅读。对于对象字段，可用 `[weakref]` 标记为弱引用，用于避免循环引用。

```tsl
type Node = class
protected
    [weakref]parent_: Node;
    children_: array of Node;
    meta_: meta_info;
end;
```

#### static 静态字段

在字段的前边加入 static 前缀，就表明为静态字段也称静态成员变量，静态字段就是全部类实例共用的字段，亦可以不用实例化来进行调用。

与对象实例无关，也就是所有对象实例均共用该变量，类似于一个作用域为该类的全局变量。

范例：

```tsl
type Thuman = class
public
static
mCount; // 人的对象数量，与对象无关
function create() begin
    mCount := (mCount?:0) + 1; // 构造的时候+1
end;
function destroy() begin//析构的时候-1
mCount--;
end;
end;
```

调用方式可如：

```tsl
class(Thuman).mCount; // 不用实例化来进行调用
```

或

```tsl
obj := new Thuman(); // 用实例化来进行调用
obj.mCount;
```

#### const 常量成员

常量成员定义的值可以是一个常数也可以是常量参与的计算。具体定义与支持的运算符可参考常量及常量成员的定义与初始化

【常量成员定义】

```tsl
type C = class
const
a := "Hello"; // 可以在声明中进行定义初始化
const
b := a + "Tinysoft";
C := a + b + "from TSL";
end;
```

【使用范围】

1、成员函数使用

2、成员函数缺省参数使用

3、子类使用

4、通过实例访问

5、通过类访问静态常量成员

示例：

```tsl
type C = class
public
const mA = 1; // 定义一个常量成员
static const mB = mA + 10; // 允许静态成员常量
function TestConst();
begin
    echo mA + mB, "\r\n"; // 允许在成员函数中使用
end;
function TestConstInParam(b = mB); // 允许在缺省参数值中使用
begin
    echo b, "\r\n";
end;
end;
```

调用：

```tsl
Cinstance := new C();
Cinstance.TestConst();
Cinstance.TestConstInParam();
echo Cinstance.mA, "\r\n";
echo class(C).mB, "\r\n";
```

打印结果：

12

11

1

11

### 方法

#### 方法简介

方法定义一个类的行为，是一个和类相关联的函数，调用一个方法需指定它作用的对象（若是类方法，则指定类），比如，

SomeObject.DoSomething

调用 SomeObject 的 DoSomething 方法。

#### 声明和实现

有 2 种方法为类添加方法。

在类声明中，可以直接定义方法的声明和实现，

或者只在类中定义方法的声明，在类声明后的某个地方（必须属于同一模块）定义它的实现，实现的时候在方法名称前加“类名.”。这种方法称为为外联，上一种方法称为内联.

示例：

```tsl
type myClass = class
function F1()
;
begin
    writeln("内联");
end;
function F2()
;
end;
function
myClass.F2
();
begin
    Writeln("外联");
end;
```

外联声明时，方法名总是使用类名进行限定,形式为：类名.方法名。在方法的头部必须重新列出类声明时的参数，名称可以与声明时的不同，但是参数的顺序必须完全相同。

##### 内联与外联

如果函数的实现在类体内，叫内联方法。

如果函数的实现在类体外，叫外联方法。

示例：

```tsl
type TSamClass = class
public
function MethodInside(); // 内联方法--声名+实现
begin
    return "Inside";
end;
function MethodOutSide(); // 仅声名
end;
function TSamClass.MethodOutSide(); // 外联方法--具体实现
begin
    return "outside";
end;
```

外联声明时，方法名总是使用类名进行限定,形式为：类名.方法名。在方法的头部必须重新列出类声明时的参数，参数名称可以与声明时的不同，但是参数的顺序必须完全相同，

##### 静态方法声明和调用

静态方法与静态字段（静态成员变量）

所有静态方法就是与对象实例无关的方法，其定义为在函数的前边加上 class 前缀，也称类方法。类方法定义了类的行为，与类的实例无关。

而静态成员则是在变量的前边加上 static 前缀，表明该成员变量与对象实例无关，也就是所有对象实例均共用该变量，类似于一个作用域为该类的全局变量。

在静态方法中禁止使用类的非静态的成员变量。

类方法（静态方法）的声明：

声明类方法时 function 前面加 class.形式为：Class Function
FunctionName([p1,p2,…))

如果是外联实现，也在实现前面加 class，形式为：Class Function
ClassName.FunctionName([p1,p2,…))

类方法（静态方法）的调用：

在实例中的调用方法为： class(类名).方法名.形式为：Class(myClass).FunctionName([p1,p2,…])

静态方法与静态成员范例

```tsl
type Thuman = class
public
static mCount;
// 人的对象数量，与对象无关
class function getCount()
; // 得到人对象的数量，与对象无关
begin
    return mCount;
end;
function create();
begin
    mCount := (mCount?:0) + 1; // 构造的时候+1
end;
function destroy();
begin//析构的时候-1
mCount--;
end;
end;
```

静态方法与静态成员的访问

```tsl
class(Thuman).mCount := 100; // 将Thuman类的静态字段mCount的值指定为100
echo class(Thuman).mCount; // 输出静态字段mCount的值
hA := new Thuman(); // 创建一个实例
echo class(Thuman).getCount(); // 调用静态方法
```

打印结果：

100

101

##### 对象的方法调用

1、如果被类内部其他的方法调用或子类中的方法调用，直接调用方法名就可以了。

2、如果在类的外部被调用，需要加上所在类型的对象名：对象名.方法名；

如调用 F1()的方法是

```tsl
myObj := createObject("myClass");
myObj.F1();
```

3、在类内部调用方法，若在父类中存在同名方法，默认是优先调用当前类，

若希望调用父类方法，则可使用 Inherited 关键字进行指定。

若希望调用指定父类方法，则可使用 class(父类名).方法名的模式调用。

###### Inherited

Inherited 是一种调用父类的巧妙的实现，这个实现和 Object
pascal 遵循相同的规则。由于 tsl 支持多重继承，因而 Inherited 会优先调用第一个继承的父类，如果没找到则会遍历之后继承的类。

Inherited 和 java 的 super 有一定的类似之处，但又不相同， java 的 super 可以表达成父类，也可以调用父类的方法，而 Inherited 都是调用父类的方法，而且单独 Inheritd 的写法，在父类不存在方法的时候不会出错，这样的特性非常便于桌面应用开发里的子类窗口的消息事件响应。

用法有两种：

方式一：在方法中，增加 inherited;则表示执行父类中存在的与当前方法名同名同参数的方法，若父类中查找不到，不报错，即什么也不做，继续向下执行。

方式二：使用如 inherited
func(a,b,c);的模式调用方法，表示调用父类中的该方法，若存在多父类，则按顺序查找，若父类中查找不到，则报错。

例如：

```tsl
type Base1 = class
function Method1(s, a, b); virtual;
begin
    s := s$"-Base1-Method1-"$(a + b);
end;
function Method2(a, b);
begin
    return "Base1-Method2-"$(a * b);
end;
end;
type SubClass = class(Base1)
function Method1(s, a, b); override;
begin
    Inherited; // 优先调用父类中Method1(s,a,b)方法，找不到不报错
    s := s + "->SubClass";
    s2 :=
    Inherited Method2(a, b); // 指定调用父类中的Method2(a,b)方法，找不到会报错
    s := s + "->" + s2;
    return s;
end;
function Method2(a, b);
begin
    return "SubClass-Method2-"$(a / b);
end;
end;
```

调用：

```tsl
obj := new SubClass();
return obj.Method1("S", 2, 50);
```

返回结果为：S-Base1-Method1-52->SubClass->Base1-Method2-100

即：Inherited;语句执行父类 Base1 中同名同参数方法 Method1(s,a,b)

Inherited
Method2(a,b);执行父类 Base1 中指定方法 Method2，而非当前类 SubClass 的 Method2 方法

#### 覆盖(override)

由于继承，子类具有了父类全部非私有的成员（字段、方法、属性），但是子类也可以对基类的方法进行重写。这种方法就叫做覆盖(override),具体的做法如下，

首先基类的方法用 virtual 关键字虚函数，告诉编译器允许子类覆盖：

```tsl
type BaseClass = class
Funciton F();
virtual;
// 虚方法，允许覆盖
begin
    // …
end;
end;
```

接下来，在子类中将将要覆盖的方法用 override 关键字标记为覆盖了基类的方法。它的参数的顺序（若有的话）必须和基类相同。

```tsl
type SubClass = class(BaseClass)
Funciton F();
override;
// 覆盖虚方法，即重写
begin
    // …
end;
end;
```

这样子类的方法 F 就覆盖了基类的方法 F，当 SubClass 的实例调用 F 方法时，会执行子类中重新定义的方法。

若需要调用父类被覆盖的方法，可以用 Class(父类).方法来调用

#### 多态

多态是面向对象的重要特性,简单点说:“一个接口，多种实现”，就是同一种事物表现出的多种形态。

编程其实就是一个将具体世界进行抽象化的过程，多态就是抽象化的一种体现，把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。

通过继承可以实现多态。父类中调用被覆盖的方法，如果当前对象是子类的实例，那么实际调用的是子类的方法，而非父类的方法。

通过继承，一个类可以用作多种类型：可以用作它自己的类型、任何祖先类型，当把子类型当作祖先类型时，调用被覆盖的方法，实际调用的是子类本身的方法，而非基类型的方法。

示例：

```tsl
program test;
type Figure = class
function Draw();
virtual;
// 虚方法，可以被覆盖
begin
    Writeln("draw Figure");
end;
function DrawAction();
begin
    Draw();
end;
end;
type Ellipse =
class(Figure)
function Draw();
override;
// 重写父类方法
begin
    Writeln("draw Ellipse");
end;
end;
begin
    F := CreateObject("Figure");
    F.DrawAction(); // 输出 draw Figure
    E := CreateObject("Ellipse");
    E.DrawAction(); // 输出 draw Ellipse
end.
```

上面的示例中：

类 Ellipse 的方法 Draw,重写了父类 Draw 这个虚方法，父类的 DrawAction 调用了 Draw 方法，当对象调用 DrawAction 方法时，实际上是间接调用了 Draw 方法。

当父类的对象间接 Draw 方法时：执行的是父类的方法。

```tsl
F := CreateObject("Figure");
F.DrawAction(); // 输出 draw Figure
```

当子类的对象间接用 Draw 方法时：执行的是子类的方法：

```tsl
E := CreateObject("Ellipse");
E.DrawAction(); // 输出 draw Ellipse
```

如果要强制子类调用父类的方法。需要用下面的方式：

```tsl
class(BaseClass, SubObject).FunctionName.
```

上面的事例中，

```tsl
E := CreateObject("Ellipse");
class(Figure, E).Draw()
;
```

执行的却是父类的 Draw 方法.输出：输出 draw Figure

#### 隐藏

如果在子类中重写父类的方法确没有使用 override 标志符，子类的方法隐藏了基类的方法，而非覆盖。

父类中调用覆盖的方法时，实际执行的是父类的方法。如果当前对象是子类的实例时，执行的还是父类的方法。因为方法在子类型中给隐藏了，而没有被覆盖。

示例：

```tsl
program test;
type Figure = class
function Draw();
begin
    Writeln("draw Figure");
end;
function DrawAction();
begin
    Draw();
end;
end;
type Ellipse = class(Figure)
function Draw(); //
// 注意没有override
begin
    Writeln("draw Ellipse");
end;
end;
begin
    F := CreateObject("Figure");
    F.DrawAction(); // 输出draw Figure
    E := CreateObject("Ellipse");
    E.DrawAction(); // 输出draw Figure
end.
```

与上节的例子不同，子类中定义 Draw 方法没有使用 override 关键字，说明子类的 Draw 方法隐藏了父类的 Draw 方法.

在父类在中调用 Draw 方法时，始终执行的是父类的 Draw 方法.

#### 重载(overload)

TSL 可以为一个类声明相同的名称不同参数的多个方法，这叫做重载。

每个重载方法声明后面加关键字 overload，并且它们必须有不同的参数列表。

形式为：

```tsl
function F();overload;
```

示例：

```tsl
type BaseClass = class
public
function Display(str, str2);overload;
begin
    writeln(str, ' ', str2);
end;
function Display(str); overload;
begin
    writeln(str);
end;
end;
```

上面的代码为 BaseClass 声明了 2 个 Display 方法。可以按照 2 种方式调用，可以在子类中重载也可以重载父类的方法。

示例：

```tsl
program test;
type BaseClass = class
public
function Display(str);
begin
    writeln(str);
end;
end;
type SubClass = class(BaseClass)
function Display(str, str2);overload;
begin
    writeln(str, ' ', str2);
end;
end;
begin
    SC := createObject("SubClass");
    SC.Display("overload");
    SC.Display("overload", "test");
end.
```

上面的例子中 SubClass 继承自 BaseClass,而 SubClass 的 Display 方法重载了父类 Display 方法。

原理：SubClass 通过继承实际上拥有了方法 Display(str)方法，然后通过 overload 重载了本身的方法，于是 SubClass 具有了 2 个不同参数的 Display 方法。

#### 构造函数

构造函数是一个特殊的方法，用来创建和初始化一个实例对象。

声明一个构造函数就像声明一个函数一样，可以无参数也可以有参数，不同的是方法名必须是 create，在创建实例时会自动查找适合的构造函数。

```tsl
function Create()
```

TSL 总是为一个类生成默认的公有(public)的 create 方法, 如果显式没有为类声明 public
create，对象初始化时就使用默认的 create 方法，如果用户声明了 public
create 方法，对象初始化时就执行用户定义的方法。Create 不可以声明为私有(private)的和受保护(protected)的方法,否则对象初始化时不执行自己声明的方法.

Create 方法可以被重载(overload)几个不同的定义。

由于使用 CreateObject 方法创建对象，Craete 方法的返回值将被忽略。

示例：

示例是一个简单的日历类，说明了构造函数的重载,初始化时如果调用 Create 方法，设置为当前日期,否则可以指定具体的年月日

```tsl
program test;
type Calandar = class
year;
month;
day;
function Create();overload;
begin
    Create(YearOf(Date()), MonthOf(Date()), DayOf(Date()));
end;
function Create(y, m, d);overload;
begin
    year := y;
    month := m;
    day := d;
end;
end;
begin
    C := CreateObject("Calandar");
    // C:=CreateObject("Calandar",2008,8,8);也可以指定具体的年月日
    writeln(C.year, C.Month, C.Day);
end.
```

构造函数的覆盖：

构造函数可以在子类中被覆盖，如果显示声明了构造函数，为了是成员初始化，一般在新的构造函数中都要求实现基类的构造函数

```tsl
// 调用 class(BaseClass).Create();
```

#### 析构函数

对象销毁时候执行的方法。声明一个析构函数就像声明一个一般的函数，必须以关键字 Destroy 命名的函数,且无参数，当对象释放时会自动调用。

```tsl
function destroy();
```

#### 指定类

如果要指定执行某个类中的方法，需要用 Class 指定类名。

如果在类的内部，或者执行类方法，格式为：

```tsl
class(ClassName).FunctionName;
// 表示执行类 ClassName 中的方法 FunctionName;
```

如果是对实例对象，希望实例对象执行某个祖先类的方法，格式为：

```tsl
class(ClassName, ObjectName).FunctionName;
```

表示实例对象 ObjectName 执行祖先类 ClassName 的 FunctionName 方法。

#### Self

在面向对象中，self 有多种用途：

第一种：在指定方法时，可以通过 self 指向当前的实例对象。

第二种：self(N)方式，可以在类中创建当前类对象或创建实例所属对象。

第一种用途：

在实现方法时，标志符 Self 引用方法所属的对象，如果是类方法，Self 引用方法所属的类。

格式为：

```tsl
self.functionName();
```

编译器会自动匹配所调用的方法，Self 方法一般可以省略。

有一点注意的是：

Self 总是指向当前的实例对象。而不是 Self 所在的类的实例对象。与继承混合使用是需要注意。

示例：

```tsl
program test;
type A = class
function F(); virtual;
begin
    self.F2();
end;
function F2();virtual;
begin
    Writeln("A.F2");
end;
end;
type B = class(A)
function F2();override;
begin
    Writeln("B.F2");
end;
end;
begin
    AA := CreateObject("A");
    AA.F(); // 输出 A.F2;
    BB := CreateObject("B");
    BB.F(); // 输出 B.F2()
end.
```

说明：

```tsl
AA := CreateObject("A");
AA.F(); // 输出 A.F2;
// 创建了类型 A 的实例对象，F()中的 Self.F2(),表示调用的是类型 A 的 F2();
BB := CreateObject("B");
```

BB.F();//输出 B.F2()

以上代码创建了类型 B 的实例对象,F()中的 Self.F2()表示调用类型 B 的 F2()

Self 的这种特性在某些情况下会出现理解上的歧义，如果想固定 Self 的意义，让他只表示当前的类。需要用 Class(ClassName).FunctionName 做替换。

```tsl
// 以上代码中的 Self.F2()需要换成 class(A).F2();
```

那么以上代码输出

A.F2

A.F2

第二种用途：

在实现方法时，可以通过 self(1)创建一个当前实例所属对象的实例对象。

self(0)等同于 self()即创建一个当前方法所在类的实例对象。

如：

```tsl
function Test_Ooself();
begin
    obj := CreateObject('TestSelf');
    obj.Test1();
end;
type TestSelf = class(TDTestFClass)
function Test1();
begin
    return V1();
    return V0();
end;
end;
type TDTestFClass = class()
public
function return V1();
begin
    ss := self(1); // 创建当前实例所属类的实例对象
    echo "TDTestFClass-return V1 ", ss.classinfo()['classname'];
end;
function return V0();
begin
    ss := self(0); // 创建当前类的对象
    echo "TDTestFClass-return V0 ", ss.classinfo()['classname'];
end;
end;
```

打印结果如下：

TDTestFClass-returnV1 testself

TDTestFClass-returnV0 tdtestfclass

注：示例中 ss.classinfo()['classname'];是获取 ss 所属对象的类名

#### 类方法

类方法是作用在类而不是对象上面的方法（不同于构造函数）。类方法的定义必须以关键字 class 开始

类方法用于创建无需创建类的实例就能够访问的方法。类方法可用于分离独立于任何对象标识的行为：无论对象发生什么更改，这些函数都不会随之变化。

形式为：

```tsl
type ClassName = class
class function FuncName();
begin
    // 方法实现
end;
end;
```

在类方法的定义部分，Self 表示调用方法的类（它或许是定义方法的类的一个派生类）。由于类方法与实例对象无关，所以，你不能使用 Self 访问字段、属性和实例的方法，但能调用构造函数和其它类方法。

类方法可以通过类引用来调用

```tsl
class(类名).类方法
```

示例：

上例中，可以

```tsl
class(ClassName).FuncName();
```

注：

一个类方法也可以被当作实例方法使用，使用方法和实例方法完全一样。方法的内部也可以调用实例的字段、属性和方法，如果方法调用了这样的数据或方法，就不能当作类方法使用了，否则的话会出错。

在 Tsl 中，字段和属性不支持这种静态的属性方法.

静态的字段可以通过全局变量实现。

更多可参考：静态方法声明和调用

### 属性 Property

属性是这样的成员：它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样使用属性，但实际上它们是称作“访问器”的特殊方法。这使得可以轻松访问数据，此外还有助于提高方法的安全性和灵活性。

语法：`Property PropertyName[(ParamList)] [read fieldOrMethod][write fieldOrMethod][Index IndexValue]`

属性名后可指定类型，语法：`property Name: Type read fieldOrMethod [write fieldOrMethod]`。类型注解不参与编译检查，可任意命名。

```tsl
property Components: DocxComponentsFacade read components_;
```

其中，

1、关键字 Property 关键字表示开始声明了一个属性；

```tsl
2、PropertyName 是自定义合法的属性名，可以带参数，写法如 PropertyName(a, b);
```

3、每个属性至少有一个读限定符或一个写限定符，或两者都有，它们称为访问限定符，具有以下的格式:

read fieldOrMethod

write fieldOrMethod

fieldOrMethod 可以是成员变量，也可以是成员方法。

4、在 TSL.INI 支持，一旦设定该选项为 1，则任何域的 property 都可被访问，无论是 public 还是 protected,private。默认情况下这种违反规则是不被允许的。

[Compatible]

PrivatePropertyAccess=1

5、属性可以具有 Private,Protected 或 public 可见性，默认为 public.

6、如果单有读限定符，表示属性只读；如果单有写限定符,表示属性只写。

7、如果它是在祖先类中声明的，则它对派生类必须是可见的，则 fieldOrMethod 不能是私有的字段或方法；

8、在读限定符中，若 fieldOrMethod 是一个方法，它须是一个小于等于定义中参数数量的函数

9、在写限定符中，若 fieldOrMethod 是一个方法，属性的设置值会以参数方式送入成员方法，即它须是一个与定义中参数数量多一个的方法。

10、属性可以在派生类中给重新定义.

示例：

```tsl
program test;
type myDate = class
private
_year;
_month;
_day;
function SetMonth(value);
begin
    if value > 0 and value < 13 then _month := value;
end;
public
// 不带参数的定义
property Month read _month write setMonth; // 读时访问成员变量_month，写时调用成员方法setMonth
// 带参数的定义
property DateV(y, m) read getDateV write setDateV;
function getDateV();
begin
    return _year * 10000 + _month * 100 + _day;
end;
function setDateV(y, m, d); // 比定义中多一个参数
begin
    _year := y;
    SetMonth(m);
    _day := d;
end;
end;
begin
    D := CreateObject("myDate");
    D.Month := 7; // 写
    echo D.Month; // 读
    D.DateV(2025, 8) := 10; // 写
    echo D.DateV(); // 读
end.
```

打印结果：

7

20250810

### 索引器(index)

简单说来，所谓索引器就是一类特殊的属性，通过它们你就可以像引用数组一样引用自己的对象。索引器通常用于对象容器中为其内的对象提供友好的存取界面.

显然，这一功能在创建集合类的场合特别有用，而在其他某些情况下，比如处理大型文件或者抽象某些有限资源等，能让类具有类似数组的行为当然也是非常有用的。

索引器的定义方式为：

Property PropertyName Index IndexValue read ReadMethod write WriteMethod

与属性不同的是，其中读写限定符必须是方法，不能是字段。而且

读限定符中的方法声明必须带有一个参数，这个参数指向索引器的索引。

写限定符的方法声明必须带有 2 个参数，第一个参数是索引器的索引，第二个是要设置的值。

与数组的区别，调用索引器的时候用圆括号，不能用方括号。

与 Object
pascal 不同的地方：TSL 的索引器的索引值除了支持整数以外，还可以支持字符串。

实例：

```tsl
program test;
type A = class()
arr;
function create()
begin
    arr := array();
end;
function rIndex(i);
begin
    return arr[i];
end;
function wIndex(i, value);
begin
    arr[i] := value;
end;
property idx read rindex write windex;
end;
begin
    AA := createobject("A");
    AA.idx(0) := "abc";
    writeln(AA.idx(0));
end.
```

上面的示例演示了索引器的使用方法。对类 A 的数据 arr 的操作，不必通过对象 AA.arr 来设置或读取，只要通过索引器即可。索引器为 A 中的数据提供友好的存取界面。

我们可以把索引器的一个索引固定为属性，就需要下面的定义方法：

Property PropertyName Index IndexValue read ReadMethod write WriteMethod

用 index n 指定索引的位置

上例中可声明

```tsl
property idx0 index 0 read rindex write windex;
```

表示 idx0 表示专门对索引器中的位置 0 进行对写，也就是对 arr[0]进行操作。

当然，假使有需要，索引也可以使字符串，那样，例如：

```tsl
property idx0 index"High school"read rindex write windex;
```

### TSL 对象的创建

#### New

参考 TSL 内置对象使用大全 CreateObject

#### CreateObject

参考 TSL 内置对象使用大全 New

### 类信息

判定一个对象是否是某个类的实例，可以用 is 判断符。

```tsl
class(类名)或者 FindClass(类名字符串)可以获得一个类的类型。
```

CreateObject 支持通过指定类的类型创建实例。

如：

```tsl
obj1 := new T1();
Echo obj1
is class(T1)
; // 判断对象是否是指定类的实例
C1 :=
findclass("t1")
; // 获取指定类的类型
Echo obj1
is C1
;
obj2 :=
CreateObject(C1)
; // 通过指定类的类型创建实例
```

#### FindClass

范例

范例 01：通过查找类运行类方法

示例：

```tsl
classA := FindClass("A");
```

classA 指向了类 A，可以通过 ClassA 调用 A 的共享方法。但是不能调用实例方法，因为 ClassA 没有指向实例对象。

```tsl
// 有类：
type ClassA = class()
class function fucA();
begin
    return "ClassA";
end;
end;
// 调用：
return findclass("ClassA").fucA();
```

返回：ClassA

范例 02：将实例对象强制转成指定父类的实例

```tsl
// 有父类：
type ClassA = class()
function fuc();virtual;
begin
    return "ClassA";
end;
end;
// 有子类：
type ClassB = class(ClassA)
function fuc();virtual;
begin
    return "ClassB";
end;
end;
// 调用：
objb := new ClassB();
obj := findclass("ClassA", objb); // 将objb对象强制转换成ClassA的实例对象
return obj.fuc();
```

返回：ClassA

#### ClassInfo

范例

范例 1：返回当前实例对象所属类的定义信息

```tsl
obj := createobject("TSBackTesting");
return obj.classinfo();
```

范例 2：返回实例对象所属类的对象类型。

```tsl
obj := createobject("TSBackTesting");
oa2 := obj.classinfo(1); // oa2是一个类的类型
obj2 := createobject(oa2); // 通过类类型构造一个实例对象
return obj2.fbegt;
```

#### objectstate

范例

```tsl
oa := new ca("abc");
echo "\r\n构造已经完成", objectstate(oa);
type ca = class
static sca;
function create(n);
begin
    sca := self;
    echo "\r\n构造中:",
    objectstate(self)
;
end;
end;
```

打印结果：

构造中:1

构造已经完成 2

#### tslassigning

如在给属性的赋值方法中打印这个状态值

```tsl
o := new ca();
o.c := 3; // 给对象的属性赋值
return o.c;
type ca = class
value;
property c read getc write setc;
function setc(v); // 对象赋值中
begin
    echo "c 的写操作>", tslassigning, "<<<<";
    value := v;
end;
function getc();
begin
    echo "c 的读操作>", tslassigning, "<<<<";
    return value;
end;
end;
```

打印结果：

c 的写操作>1<<<<

c 的读操作>0<<<<

#### 对象的引用计数

A：对象的赋值均会将对象的引用计数加 1，当引用计数为 0 的时候才会释放。

```tsl
O1 := new T1(); // 创建对象，引用计数=1
O2 := O1; // 对象引用计数加1
O1 := 0; // 对象引用计数减1
echo "O1 to Zero";
O2 := 0; // 对象引用计算减1，为0，即释放对象
echo "O2 to Zero";
type T1 = class
function destroy();
begin
    echo "destroy";
end;
end;
```

### IS 关键字

关键字 IS 用户判断一个对象是否是某个类的实例。返回值为 Bool 类型。一个子类属于所有它祖先类的类型。

示例：

```tsl
program test;
type A = class
end;
type B = class(A)
end;
type C = class(B)
end;
begin
    CC := CreateObject("C");
    Writeln(CC is class(C)); // 输出 1
    Writeln(CC is class(A)); // 输出 1
    Writeln(CC is class(B)); // 输出 1
end.
```

### 函数信息

#### FindOverLoad

范例

```tsl
type TestClass = class
function fun(p1, p2);overload;
begin
    return p1 + p2;
end;
function fun(p1);overload;
begin
    return 'a';
end;
class function fun2(p1, p2);
begin
    return p1 + p2;
end;
end;
// 调用有两个参数的重载方法
t := findoverload(2, "fun", CreateObject('TestClass'));
return t.do(1, 2); // 返回3
```

#### TSLObjects

范例

现有类 TestClass01，其代码如下：

```tsl
type TestClass01 = class()
value;
function create(_value)
begin
    value := _value;
end;
class function add(x, y)
begin
    if ifnumber(x) and ifnumber(y) then return x + y;
    else return 0;
end;
end;
```

范例 01：获取当前运行环境下所有对象的引用计数信息

```tsl
// 创建对象，初始引用个数均为1
objA := new TestClass01(100); // objA-直接引用：1，间接引用：1
// 将对象赋值给其它变量，增加objA的直接引用数
objC := objA; // objA-直接引用：2，间接引用：1
objB := new TestClass01(101); // objB-直接引用：1，间接引用：1
// 通过对象查找类中的方法，增加objB的间接引用
funcA := findfunction("add", objB); // objB-直接引用：1，间接引用：2
funcB := findfunction("add", objB); // objB-直接引用：1，间接引用：3
// 通过类名查找类中的方法，没有引用对象，已有对象引用数不变
funcC := findfunction("add", class(TestClass01));
return TSLObjects(0);
```

范例 02：获取当前运行环境下所有对象信息以及原对象，并通过该结果访问其属性、方法

```tsl
objA := new TestClass01(100);
objB := new TestClass01(101);
objsInfo := TSLObjects(1);
newObjA := objsInfo["TestClass01"][1, "obj"];
newObjB := objsInfo["TestClass01"][0, "obj"];
// 获取到的对象，其属性和方法与先前直接创建出的对象一致
v1 := newObjA.value;
v2 := newObjB.value;
t1 := newObjA.add(1, 2);
t2 := newObjB.add(3, 4);
return array(v1, v2, t1, t2);
```

#### FindFunction

在对象或类中查找方法：

F：=FindFunction(FunctionName, Object or Class)

表示在 Object 对象或 Class 中查找名称为 FunctionName 的函数。F 指向找到的函数。

第二个参数可以是

1）对象，必须预先实例化一个对象,之中方法可以用于查找实例方法或类方法。

2）类，格式为：Class(类名)。这种方法只能差找类方法，不能查找实例方法

找到的函数可以用 do 方法执行；

F.do();表示执行找到的方法，如果函数有参数，把参数传给 do 方法

```tsl
program test;
type A = class()
function F()
begin
    writeln("f");
end;
class function F2();
begin
    writeln("f2");
end;
end;
begin
    // 从对象中查找实例方法
    AA := createobject("A");
    F := FindFunction("F", AA);
    F.do();
    // 从类中查找类方法
    F2 := FindFunction("F2", class(A));
    F2.do();
end.
```

#### ThisFunction

```tsl
F := ThisFunction(Object or class.function);
```

查找方法的又一个方法；参数格式为：

1. 对象名.方法名,如果创建了对象，可用这种方法。

2. class(类名).方法名，如果是类方法，而且没有穿件对象，可以这样使用。

```tsl
program test;
type A = class()
function F()
begin
    writeln("f");
end;
class function F2();
begin
    writeln("f2");
end;
end;
begin
    // 从对象中查找实例方法
    AA := createobject("A");
    F := ThisFunction(AA.F);
    F.do();
    // 从类中查找类方法
    F2 := ThisFunction(class(A).F2);
    F2.do();
end.
```

#### FunctionInfo

调用方法：

方法.FunctionInfo

获取函数的信息。返回存放函数信息的字符串下标数组。

| 下标         | 数据类型 | 描述                     |
| ------------ | -------- | ------------------------ |
| functionname | String   | 方法名                   |
| parameter    | Array    | 方法参数                 |
| returntype   | Stirng   | 函数返回值               |
| returndim    | Integer  | 数组的维度,0表示不是数组 |
| calltype     | Integer  | 调用类型                 |
| classname    | Stirng   | 所在的类名               |

其中：parameter 下标所对应的数组也是字符串下标数组。

下标

| 下标 | 数据类型 | 描述       |
| ---- | -------- | ---------- |
| nme  | String   | 参数名称   |
| tpe  | String   | 参数类型   |
| dm   | Integer  | 数组的维度 |
| out  | Integer  |            |

注意：

可以显式为参数或返回值定义类型。当定显式义了类型后以上提到的数据类型将会是自定义定义的值，否则是空字符串，这种用法不会真正做类型检查或转化，只是通知 FunctionInfo 中的数据类型，通常用户 Web
service 的构建。

### 算符重载

TSL 的对象如果要使用算符进行计算，就需要用到算符重载，算符重载可以重载掉 TSL 的算符，在 TSL 的类中要实现算符重载的方法。

算符重载支持 TSL 开发的类，也支持用 C++等开发的二进制类。

#### Operator

#### 算符重载的案例

下边是一个复数的例子，范例中重载了+和<运算符：

```tsl
type Tcomplex = class
vReal; // 实部
vImaginary; // 虚部
function operator + (data); // 单参数，即只支持obj+10，不支持10+obj
begin
    r := new Tcomplex();
    if ifnumber(data) then r.vReal := vReal + data
    else begin
        r.vReal := vReal + data.vReal;
        r.vImaginary := vImaginary + data. vImaginary;
    end;
    return r;
end;
function operator < (data, isLeft); // 双参数，支持obj<10的判断，也支持10<obj的判断
end;
function operator Tcomplex. < (data, isLeft);
begin
    // echo "isLeft:",isLeft; // 对象是否在左边
    if ifnumber(data) then V := vReal < data
    else V := (vReal ^ 2 + vImaginary ^ 2) < data.vReal ^ 2 + data.vImaginary ^ 2;
    if not isLeft then V := not V;
    return V;
end;
```

调用测试：

```tsl
t1 := new TComplex();
t1.vReal := 10;
t1.vImaginary := 100;
t2 := t1 + 10; // 不支持t2 := 10+t1;
echo "t2.vReal:", t2.vReal;
echo "t1<5:", t1 < 5;
echo "t1<300:", t1 < 300;
echo "5<t1:", 5 < t1; // 对象在右边
```

打印结果：

t2.vReal:20

t1<5:0

t1<300:1

5<t1:1

#### 面向对象的 Operator 重载支持[]

用[]或者[0]重载读取过程，即实现指定下标时取值的操作,用[1]重载写入过程，即实现指定下标进行赋值的操作。

重载读取定义：operator 或 operator0

重载写入定义：operator1

其中，idx 表示当前下标值，支持整型与字符串

重载读取中，s 表示当前层级，中间层从 0 开始，依次为 0，1，2，3，....，最后一层时为-1。

重载写入中，v 表示写入的值，但在中间层时它是一个 none 类型，层级由 none 类型的值进行确定，最后一层时为写入的值。

[]重载时允许多级，重载中返回对象表示继续下一层

在[0]算符重载时，通过第二个参数可判断当前层级，当小于 0 时表示当前处于最后一层，重现实现中返回对象表示继续下一层，否则访问结束。

在[1]算符重载时，可通过 ifnone(v)判断第二个参数，若为真，表示为中间级，此时实现中返回对象表示继续下一层，判断为真时当前处于最后一层，此时可对该参数值进行赋值操作。

对象算符重载数组 set 算符时级别的获取与当前状态的判定：

getnone 函数可以获得 none 类型包含的 int 值。

ifnone(v,i)可以判断是否属于 none 并且所含整数位 i。

当对象重载下标写入行为时，若当前为中间级，则第二个参数为 none 类型，此时该 none 类型的值为下标的级别，从 0 开始，依次为 0,1,2,3....。如果写入的过程出现错误，则调用写入方法进行重载，此时进入的 none 类型值为-1,这样做方便数据库等事务的实现。

例如：单层的实现

```tsl
type bb = class
data;
public
function create(v);
begin
    data := v;
end;
function operator;
begin
    return data[index];
end;
function operator1;
begin
    echo "set->", index, "->", v;
    data[index] := v;
end;
end;
```

调用测试如：

```tsl
t := array(1, 2, 3, 4, 5);
b := new bb(t);
a := b[2]; // 取值
echo "b[2]:", a;
b[3] := 999; // 重新赋值
echo "b.data:", tostn(b.data);
```

打印如下：

b[2]:3

set->3->999

b.data:

array(1,2,3,999,5)

##### 对象[]重载时允许多级的应用示例

示例：对象中[]算符重载多级的实现示例

```tsl
type aa = class
data;
indList;
public
function create(v);
begin
    data := v;
    indList := array();
end;
function operator0; // 多级访问
begin
    // s1<0时，最后一层
    indList[length(indList)] := index;
    v := data;
    for i, ind in indList do v := v[ind];
    if s1 < 0 then begin
        indList := array();
        return v;
    end
    else return self; // 返回对象
end;
function operator1;
begin
    indList[length(indList)] := index;
    if ifnone(v) then return self; // 返回对象，存在多级
    sv := 'data';
    for i, ind in indList do if ifstring(ind) then sv += "['" + ind + "']";
    else sv += "["$ind$"]";
    sv += " := v;";
    eval(&sv);
    indList := array();
end;
end;
```

调用测试：

```tsl
t := array(("A":1, "B":2, "C":3), ("A":11, "B":22, "C":33));
a := new aa(t);
echo a[0, "B"];
a[1, "C"] := 999;
echo tostn(a.data);
```

打印结果：

2

array(

("A":1,"B":2,"C":3),

("A":11,"B":22,"C":999))

##### 对象算符重载数组 set 算符时 none 类型的应用实例

例如，实现一个资金流入流出的账单记录，当对历史记录进行篡改时，会引发赋值出错，进行数据回滚处理。如此来展示对象算符重载数组 set 算符时，ifnone(v,i)等功能的作用

```tsl
// 资金出入的联动实现，行标下只能依次递增，否则报错回滚
type DepositMoney = class()
FMoneyMX;
FlastR; // 上行
FnewR; // 当前行
function create(v);
begin
    FMoneyMX := array();
    FMoneyMX[0]["时间"] := datetimetostr(now()); // 初始资产
    FMoneyMX[0]["当前余额"] := v; // 初始资产
    FMoneyMX[0]["出金"] := 0; //
    FMoneyMX[0]["入金"] := 0; //
    FlastR := -1;
    FnewR := 0;
end;
function operator1; // 设置值
begin
    // echo "Idx->",idx," v->",v;
    // 当赋值出错时，进入该过程，进行回滚
    if ifnone(v, -1) then //none类型，且none整数位为-1
    begin
        // 回滚到上一次的状态
        FMoneyMX := FMoneyMX[0:FlastR];
        FnewR := FlastR;
        FlastR := FlastR - 1;
        echo "发生错误-回滚到变更前状态：", tostn(FMoneyMX);
        return "Erro";
    end;
    if ifnone(v) then//中间层级
    begin
        // echo "getnone(v)->",getnone(v); // 当前级别
        if getnone(v) = 0 then begin//第一层
        FlastR := FnewR;
        FnewR := idx;
        FMoneyMX[idx, "时间"] := datetimetostr(now());
        if FnewR < FlastR + 1 then raise "不能对历史数据进行修改，当前最新行标："$FlastR;
        return self;
    end
    else raise "只支持二维数组";
end;
if idx = "出金"then
begin
    FMoneyMX[FnewR]["当前余额"] := FMoneyMX[FlastR]["当前余额"] - v;
    FMoneyMX[FnewR]["出金"] := v;
    FMoneyMX[FnewR]["入金"] := 0;
end
else if idx = "入金"then
begin
    FMoneyMX[FnewR]["当前余额"] := FMoneyMX[FlastR]["当前余额"] + v;
    FMoneyMX[FnewR]["出金"] := 0;
    FMoneyMX[FnewR]["入金"] := v;
end;
end;
function destroy();
begin
    FMoneyMX := nil;
    FlastR := nil;
    FnewR := nil;
end;
end;
```

调用测试：

```tsl
// --事务示例
obj := new DepositMoney(100);
sleep(1 * 1000);
obj[1]["入金"] := 888;
echo "入金888->", tostn(obj.FMoneyMX);
sleep(1 * 1000);
obj[2]["出金"] := 200;
echo "出金200->", tostn(obj.FMoneyMX);
sleep(1 * 1000);
obj[2]["出金"] := 100; // 此时变动下标应该是3，指定2会引发set出现，数据会进行回滚
echo "出金100->", tostn(obj.FMoneyMX);
return obj.FMoneyMX;
```

执行报错后，打印结果如下：（在 `obj[2]["出金"]:=100;` 时触发出错逻辑，数据回到赋值之前的状态）
入金 888->

array(

("时间":"2025-08-12 17:55:40","当前余额":100,"出金":0,"入金":0),

("时间":"2025-08-12 17:55:41","当前余额":988,"出金":0,"入金":888))

出金 200->

array(

("时间":"2025-08-12 17:55:40","当前余额":100,"出金":0,"入金":0),

("时间":"2025-08-12 17:55:41","当前余额":988,"出金":0,"入金":888),

("时间":"2025-08-12 17:55:42","当前余额":788,"出金":200,"入金":0))

出金 100->

发生错误-回滚到变更前状态：

array(

("时间":"2025-08-12 17:55:40","当前余额":100,"出金":0,"入金":0),

("时间":"2025-08-12 17:55:41","当前余额":988,"出金":0,"入金":888),

("时间":"2025-08-12 17:55:43","当前余额":788,"出金":200,"入金":0))

#### 面向对象的 Operator 算符重载支持++,--,+=,-=

实现示例如：

```tsl
type bb = class
data;
public
function create(v);
begin
    data := v;
end;
function operator++(v);
begin
    if v = 0 then//d := obj++;时
    begin
        r := new bb();
        r.data := data;
        r.data++;
        return r;
    end//其它情况下，都是++后的状态
    else data++;
end;
function operator--(v);
begin
    if v = 0 then
    begin
        r := new bb();
        r.data := data;
        r.data--;
        return r;
    end
    else data--;
end;
function operator += (v);
begin
    data += v;
end;
function operator -= (v);
begin
    data -= v;
end;
end;
```

调用测试：

```tsl
t := array(1, 2, 3, 999, 5);
b := new bb(t);
++b; // 自加
echo "++b后：";
echo "b.data: ", tostn(b.data);
c := b++;
echo "c := b++;后：";
echo "c.data: ", tostn(c.data);
echo "b.data:", tostn(b.data);
```

打印结果如下：

++b 后：

b.data:

array(2,3,4,1000,6)

```tsl
c := b++;后：
```

c.data:

array(2,3,4,1000,6)

b.data:

array(3,4,5,1001,7)

### 单元中的类

天软中，可以将相同功能封装到单元中，形成专用工具方法库。

```tsl
// 在单元中的类，可通过 obj:=new Unit1.Class1.Class2()的方式进行构建
```

通过 Type ClassA=Class(Unit1.Class1.Class2...)模式进行继承

#### 继承单元中的类

继承单元中的类，和继承常规类不同：

继承常规类：直接继承一个已知的类，如 Type ClassA=Class(TStringList)。

继承单元中的类：需要通过 Type
ClassA=Class(Unit1.Class1.Class2...)模式来定义类，Class 中包含“单元名.外层类名.内层类名”的完整路径形式，精确指定要继承的类位置。

具体语法如下：

```tsl
type ClassA = class(Unit1.Class1.Class2, OtherClass)
end;
```

例如：`type ClassA = class(Unit1.Class1.Class2)` 表示 ClassA 直接继承单元 Unit1 中 Class1 内部定义的嵌套类 Class2。
书写时需注意，必须完整、准确的指定从单元名到最终类名的全部路径，否则会执行失败。

注：2025-08-27 后的 NG 客户端及使用新一代 TSL 的服务器支持该功能。

示例：

定义于 Unit1 中的类结构

```tsl
unit Unit1;
interface
type Class1 = class
type Class2 = class// 嵌套类
function MethodInClass2();
class function ClassMethodInClass2();
end;
end;
initialization
finalization
end.
```

直接继承 Unit1.Class1.Class2

```tsl
type MyNewClass = class(Unit1.Class1.Class2)
function MyNewMethod();
end;
```

创建实例

```tsl
// 方式一：CreateObject
obj1 := CreateObject("MyNewClass");
obj1.MethodInClass2(); // 调用父类方法
// 方式二：new 关键字
obj2 := new MyNewClass();
obj2.MethodInClass2(); // 调用父类方法
```

查找类

```tsl
classRef := FindClass("MyNewClass");
classRef.ClassMethodInClass2(); // 调用父类中类方法
```

#### 构造单元中的类实例

构造单元中的类实例与构造常规类实例方法类似，均可使用 CreateObject 或 new 实现。

核心区别在于类标识符的指定方式：

常规类实例：只需提供类名，如 CreateObject("TStringList")或 new TStringList()。

单元中的类实例：需提供完整的类路径（格式：单元名.类名）,如 CreateObject("Unit1.Class1.Class2")或 new
Unit1.Class1.Class2()。

具体语法如下：

```tsl
obj := CreateObject("Unit1.Class1.Class2", p1, p2);
obj := new Unit1.Class1.Class2(p1, p2);
```

例如：

```tsl
obj := new Unit1.Class1.Class2();
```

表示创建单元 Unit1 中 Class1 内部定义的嵌套类 Class2 的实例，并赋值给 obj。

注：2025-08-27 后的 NG 客户端及使用新一代 TSL 的服务器支持该功能。

示例：

定义于 Unit1 中的类结构

```tsl
unit Unit1;
interface
type Class1 = class
type Class2 = class// 嵌套类
function MethodInClass2();
class function ClassMethodInClass2();
end;
end;
initialization
finalization
end.
```

创建实例

```tsl
// 方式一：CreateObject
obj1 := CreateObject("Unit1.Class1.Class2");
obj1.MethodInClass2(); // 调用方法
// 方式二：new 关键字
obj2 := new Unit1.Class1.Class2();
obj2.MethodInClass2(); // 调用方法
```

#### 预定义：ParentClassInUnit

判定当前环境是否支持继承和构造单元中的类

```tsl
{$IFDEF ParentClassInUnit}
return 1;
{$else} //否则（如果未定义）
return "当前环境不支持继承和构造单元中的类";
{$ENDIF}
```

#### 单元中的类-应用实例

现有日期相关单元 TD_DateUnit，单元中包含类 TD_DateClass、IntDate、StrDate。

```tsl
unit TD_DateUnit;
interface
type TD_DateClass = class()//父类
value; // 天软日期
function create(v);
begin
    value := isDate(v)?v:0;
end;
function isDate();overload;
begin
    return isDate(value);
end;
class function isDate(v);overload; // 是否是一个日期
begin
    try
        y := yearof(v);
        m := monthof(v);
        d := dayof(v);
        return isValidDate(y, m, d);
    except
        return 0;
    end;
end;
// --整型-日期
type IntDate = class(TD_DateClass)//内部类，继承父类
iDate; // 对应的整数日期
function create(v);
begin
    iDate := _datetoint(v);
    value := inttodate(iDate);
end;
function _datetoint();overload;
begin
    return iDate;
end;
class function _datetoint(v);overload;
begin
    if ifstring(v)then
    begin
        _iDate := datetoint(strtodate(v));
    end else
    if v < 99999 then
    begin
        isd := isDate(v);
        _iDate := isd?datetoint(v):v;
    end
    else _iDate := Int(v);
    return _iDate;
end;
end;
// --字符串-日期
type StrDate = class()//内部类，不继承父类
value; // 天软日期
sDate; // 对应的字符串日期
function create(v);
begin
    sDate := _datetostr(v);
    value := strtodate(sDate);
end;
class function _datetostr(v)
begin
    obj := new IntDate(v);
    _sDate := datetostr(obj.value);
    return _sDate;
end;
function formatS(f); // 按指定符号生成字符串日期
begin
    fs := "yyyy" + f + "mm" + f + "dd";
    return FormatDateTime(fs, value);
end;
end;
end;
implementation
Initialization
Finalization end.
```

使用示例

```tsl
dateClass := findclass("TD_DateUnit.TD_DateClass");
echo dateClass.isDate(20250912T); // 天软日期，返回值：1
echo dateClass.isDate("2025-09-12"); // 字符串日期，返回值：0
// 现有如下日期
endt := 20250912T;
// 通过intDate类，转换成整数日期
obj := new TD_DateUnit.TD_DateClass.intDate(endt);
echo obj.iDate; // 返回值：20250912
echo obj._datetoint("2025-09-12"); // 返回值：20250912
// 通过strDate类，转换成整数日期
obj := new TD_DateUnit.TD_DateClass.strDate(endt);
echo obj.sDate; // 返回值：2025-09-12
echo obj._datetoStr(20250912); // 返回值：2025-09-12
echo obj.formatS("."); // 返回值：2025.09.12
return 1;
```

打印结果如下：

### TSL 内置对象使用大全

#### TSL 内置对象使用大全简介

TSL 支持三种对象：COM 对象，TSL 内置对象，TSL 语言编写的对象。

TSL 内置对象是二进制开发的 TSL 对象，独立的 TSL 内置对象可以在 TSL 语言里继承，但是不能重载二进制的内置对象的方法。

#### FTP 对象

说明：FTP 对象通过文件传输协议（FTP）提供对文件服务器的客户端访问。FTP 对象的方法和属性作用于打开、登陆已经关闭 FTP 连接，同事用于上传、下载、重命名、删除和获得 FTP 服务器上的文件信息。

##### FTP 对象的创建

范例

范例 1：创建对象未指定配置名，手动设置 FTP 配置并连接登录服务器

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
obj.connect(); // 连接服务器
obj.login(); // 登录服务器
return obj.RetrieveCurrentDir();
// 连接并登录成功，返回当前所处目录（默认为"/"）
```

范例 2：创建对象时指定配置名，自动读取配置文件中的设置并连接登录服务器

配置文件为 Analyse.NET\PLUGIN\FileMgr.INI，配置格式如下：

[CLASS:FTP]

permit=local

[FTP Settings]

TSDN.Case.FTP:Port=20

TSDN.Case.FTP:UserName=username

TSDN.Case.FTP:Password=password

TSDN.Case.FTP=ftp.tinysoft.com.cn

```tsl
obj := CreateObject("FTP", "TSDN.Case.FTP");
return obj.RetrieveCurrentDir();
// 连接并登录成功，返回当前所处目录（默认为"/"）
```

##### FTP 对象的方法

###### Abort

范例

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
obj.connect(); // 连接服务器
obj.login(); // 登录服务器
return obj.Abort();
// 终止连接成功，返回0
```

###### ChangeDir

范例

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
obj.connect(); // 连接
obj.login(); // 登录
obj.changedir('/test'); // 将FTP服务器上的当前目录更改为"/test"
return obj.RetrieveCurrentDir();
// 修改成功，当前目录应为"/test"
```

###### ChangeDirUp

范例

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
obj.connect(); // 连接
obj.login(); // 登录
obj.changedir('/test'); // 将FTP服务器上的当前目录更改为"/test"
obj.changeDirUp(); // 跳转到当前目录的上一级目录"/"
return obj.RetrieveCurrentDir();
// 跳转成功，当前目录应为"/"
```

###### Connect

范例

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
return obj.connect();
// 连接成功，返回0
```

###### Delete

范例

删除 FTP 服务器指定文件

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
// 删除Test.txt文件
obj.delete("Test.txt");
// 验证文件是否被删除
return obj.FileDate("Test.txt", 0);
// 获取文件时间，若无该文件，返回0
```

###### DisconnectNotifyPeer

###### FileDate

范例

获取 FTP 服务器指定文件的最后修改时间

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
ret := obj.FileDate("Test.txt", 0);
return DateTimeToStr(ret);
// "2024-07-31 14:28:00"
```

###### Get

范例

从服务器下载指定文件至本机

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
obj.TryNATFastTrack := 1;
// 下载文件
st := new TFileStream("", "D:\\test\\1.txt", 65535);
obj.get("FtpTest.txt", st, 0);
// 若下载成功，可在本机对应位置查看文件
```

###### Help

###### IsServerMDTZAndListTForm

###### List

范例

查看当前服务器目录下匹配到的文件列表

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
AFiles := new TStringList();
// 获取当前目录所有.txt文件信息
obj.List(AFiles, '*.txt', true);
return AFiles.GetText();
// 字符串:
"-rw-r--r--1 1001   1001      17 Apr 09 16:07 test01.txt
-rw - r--r--1 1001 1001 0 Sep 16 2023 test02.txt
-rw - r--r--1 1001 1001 38 Jul 25 17:26 test03.txt"
```

###### Login

范例

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
return obj.RetrieveCurrentDir();
// 获取当前所在目录，若登录成功，则返回"/"
```

###### MakeDir

范例

在服务器当前目录中创建新文件夹

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
obj.MakeDir("newDir");
// 若创建成功，可在服务器中查看或使用FAQ：
List
// 查看
```

###### Noop

###### Put

范例

将本机指定文件上传至服务器

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
// 构造TStream对象
filepath := "D:\\test\\Test.txt";
st := new TMemoryStream();
st.LoadFromFile("", filepath);
obj.Put(st, "put.txt", false);
// 若上传成功，可在服务器中查看或使用FAQ：
Get
// 下载后在本机查看
```

###### Quit

###### Quote

###### ReInitialize

###### RemoveDir

范例

从服务器删除指定文件夹

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
obj.Remove("testDir");
// 若删除成功，可在服务器中查看或使用FAQ：
List
// 查看
```

###### Rename

范例

范例一：重命名文件夹

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
obj.rename("testDir", "newDir");
// 若重命名成功，可在服务器中查看或使用FAQ：
List
// 查看
```

范例二：重命名文件

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
obj.rename("test.txt", "new.txt");
// 若重命名成功，可在服务器中查看或使用FAQ：
List
// 查看
```

###### ResumeSupported

###### RetrieveCurrentDir

范例

获取当前所在目录

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
return obj.RetrieveCurrentDir();
// 字符串："/"
```

###### SetCmdOpt

###### SetLang

###### SetModTime

###### SetModTimeGMT

###### Site

###### Size

范例

查看服务器当前目录指定文件大小

```tsl
obj := CreateObject("FTP");
obj.host := "ftp.tinysoft.com.cn";
obj.port := 20;
obj.username := username;
obj.password := password;
// 连接FTP服务器
obj.connect();
// 登录
obj.Login();
return obj.Size("test.txt");
// 整数，18
```

###### Status

###### CRC

##### FTP 对象属性

###### Account

###### AUTHCmd

###### AutoLogin

###### AutoIssueFEAT

###### CanResume

###### CurrentTransferMode

###### DataPort

###### DataPortMin

###### DataPortMax

###### DirFormat

###### ExternalIP

###### Host

###### LangsSupported

###### LastCmdResult

###### ListResult

###### Passive

###### Password

###### Port

###### ProxyHost

###### ProxyPort

###### ProxyUsername

###### ProxyPassword

###### ProxyType

###### ReadTimeout

###### SupportsVerification

###### SystemDesc

###### TransferTimeout

###### TransferType

###### TryNATFastTrack

###### UseCCC

###### UseExtensionDataPort

###### UseMLIS

###### Username

###### UseTLS

###### UsingExtDataPort

###### UsingNATFastTrack

###### UsingSFTP

###### PassiveUseControlHost

###### ListenTimeout

###### TransferMode

- Account
- AUTHCmd
- AutoLogin
- AutoIssueFEAT
- CanResume
- CurrentTransferMode
- DataPort
- DataPortMin
- DataPortMax
- DirFormat
- ExternalIP
- Host
- LangsSupported
- LastCmdResult
- ListResult
- Passive
- Password
- Port
- ProxyHost
- ProxyPort
- ProxyUsername
- ProxyPassword
- ProxyType
- ReadTimeout
- SupportsVerification
- SystemDesc
- TransferTimeout
- TransferType
- TryNATFastTrack
- UseCCC
- UseExtensionDataPort
- UseMLIS
- Username
- UseTLS
- UsingExtDataPort
- UsingNATFastTrack
- UsingSFTP
- PassiveUseControlHost
- ListenTimeout
- TransferMode

#### TRSA

TRsa 类用于提供 RSA 算法。RSA 是非对称加密算法，加解密速度慢，被加密明文的长度有限制，通常用于密钥传输、证书签名等场景。TRsa 类提供的成员方法主要包括：密钥生成、密钥读写、加解密。

##### TRSA 的属性

###### PublicKey

###### PrivateKey

##### TRSA 的方法

###### GenerateKey

###### PubEncrypt

###### PriEncrypt

###### PubDecrypt

###### PriDecrypt

##### TRSA 使用说明以及范例

在使用加密和解密方法前必须确保 TRsa 对象中已经正确地设置了密钥。

GenerateKey 方法会生成一对新的密钥，而给 PublicKey 和 PrivateKey 赋值可以分别载入一个已经存在的公钥和私钥，要注意的是给 PublicKey 和 PrivateKey 中的任何一个赋值，系统都会先将公钥和私钥同时重置，然后再分别设置公钥和私钥。

公钥和私钥都以 PKCS#1 格式编码。

被加密的数据长度通常不能超过密钥字节数-11，否则函数将执行失败。例如使用 1024 位密钥，即 128 字节，那么被加密数据不能超过 117 个字节。加密后的密文数据长度和密钥字节数相同。

示例：

下述代码展示了两种加密解密过程：

1.文本内容->公钥加密->私钥对加密内容解密

2.文本内容->私钥加密->公钥对加密内容解密

```tsl
filename := 'd:\\a.txt';
size := filesize("", filename);
ReadFile(rwraw(), "", filename, 0, size, file_str); // 读取文本内容

rsa_obj := CreateObject('TRsa');
rsa_obj.GenerateKey(1024);

rsa_pubkey := rsa_obj.PublicKey; // 公钥
rsa_prikey := rsa_obj.PrivateKey; // 私钥

rsa_obj_1 := CreateObject('TRsa');
rsa_obj_1.PublicKey := rsa_pubkey;
rsa_pub_enc := rsa_obj_1.PubEncrypt(file_str); // 使用公钥加密文本内容
rsa_obj_1.PrivateKey := rsa_prikey;
rsa_pri_dec := rsa_obj_1.PriDecrypt(rsa_pub_enc); // 使用私钥解密

rsa_pri_enc := rsa_obj_1.PriEncrypt(file_str); // 使用私钥加密文本内容
rsa_obj_1.PublicKey := rsa_pubkey;
rsa_pub_dec := rsa_obj_1.PubDecrypt(rsa_pri_enc); // 使用公钥解密

return array(
    "文本内容": file_str,
    "RSA私钥": rsa_prikey,
    "RSA公钥": rsa_pubkey,
    "RSA公钥加密": EncodeRadixStr(rsa_pub_enc, "", 16),
    "RSA私钥解密": rsa_pri_dec,
    "RSA私钥加密": EncodeRadixStr(rsa_pri_enc, "", 16),
    "RSA公钥解密": rsa_pub_dec
);
```

#### TCipher

TCipher 类用于多重对称加密算法，其支持的算法有：

| mode | 算法名      | 密钥长度（字节） | IV长度（字节） |
| ---- | ----------- | ---------------- | -------------- |
| 1    | DES_ECB     | 8                | 0              |
| 2    | DEC_CBC     | 8                | 8              |
| 3    | DES_CFB     | 8                | 8              |
| 4    | DES_OFB     | 8                | 8              |
| 5    | IDEA_ECB    | 16               | 0              |
| 6    | IDEA_CBC    | 16               | 8              |
| 7    | IDEA_CFB    | 16               | 8              |
| 8    | IDEA_OFB    | 16               | 8              |
| 9    | AES_128_ECB | 16               | 0              |
| 10   | AES_128_CBC | 16               | 16             |
| 11   | AES_128_CFB | 16               | 16             |
| 12   | AES_128_OFB | 16               | 16             |
| 13   | AES_192_ECB | 24               | 0              |
| 14   | AES_192_CBC | 24               | 16             |
| 15   | AES_192_CFB | 24               | 16             |
| 16   | AES_192_OFB | 24               | 16             |
| 17   | AES_256_ECB | 32               | 0              |
| 18   | AES_256_CBC | 32               | 16             |
| 19   | AES_256_CFB | 32               | 16             |
| 20   | AES_256_OFB | 32               | 16             |

##### TCipher 类的属性

###### Mode

###### Password

###### Key

###### IV

###### KeyLength

###### LVLength

##### TCipher 类的方法

###### Encrypt

###### EncryptFile

范例：

```tsl
cipher_obj := CreateObject('TCipher', 7); // 使用DES_CBC算法
cipher_obj.Password := 'Tinysoft'; // 设定密码
// 给指定文件进行加密，并将加密后的内容写入指定文件中
cipher_obj.EncryptFile('', 'd:\\test\\test.txt', '', 'd:\\test\\test2.txt');
// 给指定文件进行解密，并将解密后的内容写入指定文件中
cipher_obj.DecryptFile('', 'd:\\test\\test2.txt', '', 'd:\\test\\test3.txt');
return 1;
```

###### Decrypt

###### DecryptFile

##### TCipher 使用说明以及范例

TCipher 类所支持的加密算法是基于数据块的对称加密算法。在使用类进行处理前，首先应设置 Mode 属性，指定算法种类。

对称加密算法加密和解密使用相同的密钥(key)和初始化向量(IV)。初始化向量的作用在于：TCipher 提供的多数加密算法为了提高加密强度，在密文的块与块之间实现了一定的关联，初始化向量为第一块设定初始关联，如果初始化向量不正确，数据会出现错误。不同算法的密钥和初始化向量的长度是不同的，TCipher 提供了 KeyLength 和 IVLength 可以取得密钥和初始化向量的长度。在加密和解密前可以通过 Key 和 IV 属性设置密钥和初始化向量，因为 key 和 IV 都是二进制的，因此应使用 hex 字符串进行设置：

```tsl
cipher_obj := CreateObject('TCipher', 2); // 使用DES_CBC算法
// 测试使用key和IV进行加密
key8 := '0102030405060708';
iv8 := 'FFFFFFFFFFFFFFFF';
cipher_obj.key := key8;
cipher_obj.iv := iv8;
```

因为 key 和 IV 是二进制的，而且在有的算法中较长，在某些应用场景中不便于读取和记忆。TCipher 还提供了 Password 属性。通过 Password 属性可以方便地设置 key 和 IV，TCipher 类会根据设置的 Password 字符串，通过哈希算法计算出一个 key 和 IV，这样加解密的双方就可以用一个 Password 实现加密和解密了。示例：

```tsl
filename := 'd:\\a.txt;
size := filesize("", filename); // 获取文件大小
ReadFile(rwraw(), "", filename, 0, size, file_str);
// 对称加密算法类
cipher_obj := CreateObject('TCipher', 2); // 使用DES_CBC算法
cipher_obj.Password := 'Tinysoft'; // 设定密码
des_cbc_str := cipher_obj.Encrypt(file_str); // 加密字符串
orig_des_cbc := cipher_obj.Decrypt(des_cbc_str); // 解密字符串
```

文件加解密过程中 src_alias 和 dest_alias 参数用于指定文件的目录别名。下面是文件加解密示例（本地执行）：

```tsl
cipher_obj := CreateObject('TCipher', 2); // 使用DES_CBC算法
cipher_obj.Password := 'Tinysoft'; // 设定密码
// 加密文件
cipher_obj.EncryptFile('', 'd:\\test.bmp', '', 'd:\\test.bmp.enc');
// 解密文件
cipher_obj.DecryptFile('', 'd:\\test.bmp.enc', '', 'd:\\test_dec.bmp');
```

#### TStringList 对象

##### TStringList 对象的创建

调用：CreateObject('TStringList')

##### TStringList 对象的属性

###### Values

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";";
obj.DelimitedText := 'A=ABC;B=123;C=456.;D=abc';
SN := obj.count;
r := array();
for i := 0 to SN - 1 do
begin
    r[i, 'name'] := obj.Names(i); // 获取name值
    r[i, 'value'] := obj.values(obj.Names(i)); // 获取指定name对应的value值
end;
return r;
```

###### StringsW

- Values
- StringsW
- CommaTextW
- ValueFromIndex
- TextW
- GetText
- GetTextW
- Capacity
- CaseSensitive
- Count
- Duplicates
- Sorted
- Strings
- CommaText
- DelimitedText
- Delimiter
- Names
- NameValueSeparator
- QuoteChar
- Text

###### CommaTextW

- Values
- StringsW
- CommaTextW
- ValueFromIndex
- TextW
- GetText
- GetTextW
- Capacity
- CaseSensitive
- Count
- Duplicates
- Sorted
- Strings
- CommaText
- DelimitedText
- Delimiter
- Names
- NameValueSeparator
- QuoteChar
- Text

###### ValueFromIndex

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";";
obj.DelimitedText := 'A=ABC;B=123;C=456.;D=abc';
s0 := obj.ValueFromIndex(1); // 读取第1个
obj.ValueFromIndex(1) := "888"; // 写入，即给指定位置的value重新赋值
return array(s0, obj.DelimitedText);
```

###### TextW

- Values
- StringsW
- CommaTextW
- ValueFromIndex
- TextW
- GetText
- GetTextW
- Capacity
- CaseSensitive
- Count
- Duplicates
- Sorted
- Strings
- CommaText
- DelimitedText
- Delimiter
- Names
- NameValueSeparator
- QuoteChar
- Text

###### GetText

- Values
- StringsW
- CommaTextW
- ValueFromIndex
- TextW
- GetText
- GetTextW
- Capacity
- CaseSensitive
- Count
- Duplicates
- Sorted
- Strings
- CommaText
- DelimitedText
- Delimiter
- Names
- NameValueSeparator
- QuoteChar
- Text

###### GetTextW

- Values
- StringsW
- CommaTextW
- ValueFromIndex
- TextW
- GetText
- GetTextW
- Capacity
- CaseSensitive
- Count
- Duplicates
- Sorted
- Strings
- CommaText
- DelimitedText
- Delimiter
- Names
- NameValueSeparator
- QuoteChar
- Text

###### Capacity

###### CaseSensitive

###### Count

###### Duplicates

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ",";
obj.Sorted := 1; // 自动排序
obj.Duplicates := 2; // 重复串出错
obj.DelimitedText := 'A=abc,B=123,C=abc,F=996,E=abd';
obj.Add("b=123"); // 插入一个重复串
return obj.DelimitedText;
```

执行报错：

###### Sorted

范例：

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'C=456.,a=abc,E=666,B=123';
echo obj.CommaText, "\r\n";
obj.Sorted := 1; // 设定排序后
echo obj.CommaText, "\r\n";
return;
```

打印结果：

C=456.,a=abc,E=666,B=123

a=abc,B=123,C=456.,E=666

差异说明排序与系统有关，Linux 中，默认是以 C 为标准，按 ASCII 大小排序或比较大小的（即区分大小写)，而 windows 中，按字母顺序进行（即不区分大小写）。

例如运行下列代码：

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'C=456.,c=A123,a=abc,A=ABC,E=666,B=123';
echo obj.CommaText, "\r\n";
obj.Sorted := 1; // 设定排序后
echo obj.CommaText, "\r\n";
return;
```

在 Windows 中打印的结果为(排序按字母顺序)：

C=456.,c=A123,a=abc,A=ABC,E=666,B=123

a=abc,A=ABC,B=123,C=456.,c=A123,E=666

在 Linux 中打印的结果为(按 ASCII 码)：

C=456.,c=A123,a=abc,A=ABC,E=666,B=123

A=ABC,B=123,C=456.,E=666,a=abc,c=A123

排序后的顺序是存在差异的。但在同一系统中，每次表现是一定的。

###### Strings

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";";
obj.DelimitedText := 'A=ABC;B=123;C=456.;D=abc';
return obj.strings(1); // 读
```

结果：B=123

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";";
obj.DelimitedText := 'A=ABC;B=123;C=456.;D=abc';
obj.strings(1) := "EF=aaa"; // 写
return obj.DelimitedText;
```

结果：A=ABC;EF=aaa;C=456.;D=abc

###### CommaText

范例

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'ABC,12,"3,a""bc"';
return array(obj.strings(0), obj.strings(1), obj.strings(2));
```

###### DelimitedText

范例

可参考 QuoteChar

###### Delimiter

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";"; // 这一句必须设置在obj.DelimitedText赋值之前
obj.DelimitedText := 'ABC,;123,;456.;abc';
SN := obj.count;
r := array();
for i := 0 to SN - 1 do r[i] := obj.strings(i);
return r;
```

###### Names

范例

可参考 values

###### NameValueSeparator

###### QuoteChar

范例

```tsl
obj := CreateObject('TStringList');
obj.QuoteChar := "&";
obj.Delimiter := ";";
obj.DelimitedText := '&AB;C,&;123,;456.;abc';
SN := obj.count;
r := array();
for i := 0 to SN - 1 do r[i] := obj.strings(i);
return r;
```

返回结果如下：第一个串中，存在分割符;，需要用指定的引用符&将当前完整的串引用起来。

###### Text

范例

```tsl
obj := CreateObject('TStringList');
obj.NameValueSeparator := " "; // 空格为间隔符
obj.Text := 'A a
B b
C c
D d
';
SN := obj.count;
r := array();
for i := 0 to SN - 1 do
begin
    r[i, 'n'] := obj.Names(i);
    r[i, 'v'] := obj.values(obj.Names(i));
end;
return r;
```

返回结果：

##### TStringList 的方法

###### ExChange

范例

将位置 0 与位置 2 的内容进行互换

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'A=ABC,B=123,C=abc';
obj.ExChange(0, 2);
return obj.CommaText;
```

返回结果：C=abc,B=123,A=ABC

###### Add

范例

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'A=ABC,B=123,C=abc';
index := obj.Add("X=DDD");
echo index;
sN := obj.count;
r := array();
for i := 0 to SN - 1 do
begin
    r[i, 'n'] := obj.Names(i);
    r[i, 'v'] := obj.values(obj.Names(i));
end;
return r;
```

其中，index 值为 3，添加后结果为：

###### Clear

###### Delete

###### Find

范例

```tsl
obj := CreateObject('TStringList');
obj.Sorted := 1;
obj.CommaText := 'C=456.,D=abc,A=ABC,E=666,F=A123,B=123';
f := obj.Find("E=666", Index);
if f then return Index;
else return '查找失败';
```

返回 4

###### IndexOf

###### Insert

###### Sort

差异说明排序结果与系统的默认排序规则有关，具体可参考： FAQ：Sorted

范例

```tsl
obj := CreateObject('TStringList');
obj.CommaText := 'C=456.,a=abc,E=666,B=123';
echo obj.CommaText, "\r\n";
obj.sort(); // 设定排序后
echo obj.CommaText, "\r\n";
return;
```

打印结果：

C=456.,a=abc,E=666,B=123

a=abc,B=123,C=456.,E=666

###### AddStrings

范例

```tsl
bj := CreateObject('TStringList');
obj.CommaText := 'A=ABC,B=123,C=abc';
obj2 := CreateObject('TStringList');
obj2.CommaText := 'A=DDD,G=666';
obj.AddStrings(obj2);
return obj.CommaText;
```

返回结果："A=ABC,B=123,C=abc,A=DDD,G=666"

###### Append

###### Assign

范例

```tsl
obj := CreateObject('TStringList');
obj.Delimiter := ";";
obj.DelimitedText := 'A=DDD;G=666';
obj2 := CreateObject('TStringList');
obj2.Delimiter := ",";
obj2.Assign(obj); // 复制
return array(obj2.Delimiter, obj2.DelimitedText);
```

返回结果：array(";","A=DDD;G=666")

###### Equals

###### GetText

###### IndexOfName

###### LoadFromFile

范例

远端用户需要本地执行这个操作

```tsl
LJ := "E:\\Test\\Case-save.txt";
obj := CreateObject('TStringList');
obj.LoadFromFile('', LJ);
return obj.CommaText;
```

返回:

A a,B b,C c,D d

###### LoadFromStream

范例

```tsl
ws := new TMemoryStream();
ws.write("A=aaa", 5);
ws.position := 0;
obj := CreateObject('TStringList');
obj.LoadFromStream(ws);
return obj.CommaText;
```

返回结果：A=aaa

###### Move

###### SaveToFile

范例

远端用户需要本地执行这个操作

```tsl
pathname := "E:\\Test\\Case-save.txt";
obj := CreateObject('TStringList');
obj.Text := TestStr1();
obj.SaveToFile('', pathName);
```

执行后本地生成 txt 文档：

###### SaveToStream

范例

```tsl
ws := new TMemoryStream();
obj := CreateObject('TStringList');
obj.Text := 'A a
B b
C c
D d
';
obj.SaveToStream(ws);
return ws.size; // 返回20
```

###### SetText

##### TStringList 对象重载[]算符

可通过 TStringList 对象直接[]下标方式访问字符串内容

例如：

```tsl
ts := new TStringList();
ts.CommaText := "aaa,bbb,222,111";
echo ts[1]; // 数字下标
ts.CommaText := "A=aaa,B=bbb,C=222,D=111";
echo ts["B"]; // 字符串下标
```

打印结果：

bbb

bbb

#### THashedStringList 对象

THashedStringList 对象的使用与 TStringList 完全相同，不同的地方是 THashedStringList 进行了 Hash，所以在检索字符串的时候的效率会比 TStringList 快很多，但是由于要建立 Hash，所以内存占用与插入效率会低于 TStringList。

参见：TStringList 对象

##### THashedStringList 对象的创建

调用：CreateObject('THashedStringList')

参见：TStringList 对象

#### TStream 对象

TStream 类型是 TMemoryStream，THandleStream，TFileStream 对象的父类，本身是一个纯虚对象，不可被直接创建。

##### TStream 的属性

###### Position

指针初始位置从 0 开始，当写入内容后指针会更新，自动指向尾部。

如：

```tsl
obj := CreateObject("TMemoryStream");
echo obj.Position;
buffer := "ABC 123";
p1 := obj.write(buffer, 10);
echo obj.Position;
p2 := obj.write(buffer, 10);
echo obj.Position;
```

打印结果为：

0

10

20

###### Size

##### TStream 的方法

###### CopyFrom

范例：复制一个 TStream 流中的局部内容

```tsl
obj := CreateObject("TMemoryStream");
buffer := "ABC-123";
obj.write(buffer, 8);
obj.position := 2; // 重新指定当前位置
obj2 := CreateObject("TMemoryStream");
cf := obj2.CopyFrom(obj, 4); // 从源流中的位置2起开始复制4个字节的内容
buffer := "xxxxxxxxxxxx"; // 12个字节
obj2.position := 0;
obj2.read(buffer, 7); // 将obj2流的内容读到buffer中
return buffer;
```

返回结果为：C-12xxxxxxxx

###### Read

范例：读流中的内容，注意需要先通过 position 设置起始位置

```tsl
obj := CreateObject("TMemoryStream");
echo "起始位置:", obj.position;
buffer := "ABC-123"; // 7个字节
obj.Write(buffer, 6); // 写入6个字节
echo "写入后的位置:", obj.position;
obj.position := 0; // 当前位置重新设置为起始位置
buffer := "xxxxxxxxxxxx"; // 12个字节
obj.read(buffer, 7); // 将流中的7个字节的内容读到buffer中
echo "读取后的位置:", obj.position;
return buffer;
```

执行后打印信息：

起始位置:0

写入后的位置:6

读取后的位置:6

返回的结果为：ABC-12xxxxxx

###### Seek

范例 01：从尾部进行移动

```tsl
obj := CreateObject("TMemoryStream");
buffer := "ABC 123";
obj.write(buffer, 8);
echo "移动前位置:", obj.position;
p := obj.seek(4, 2); // 从尾部进行移动4个字节
echo "移动后位置:", obj.position, " 返回的位置:", p;
return p;
```

打印的结果如下：

移动前位置:8

移动后位置:12 返回的位置:12

###### SetSize

###### Write

#### TMemoryStream 对象

参见：TStream 对象

##### TMemoryStream 对象的创建

调用：CreateObject('TMemoryStream')

##### TMemoryStream 的方法

###### Clear

###### LoadFromFile

范例：加载本地的 txt 的内容，注：该功能是与本地交互，因此，需要在本地执行

```tsl
LJ := "E:\\test\\TestAAA.txt";
obj := CreateObject("TMemoryStream");
obj.position := 0; // 设置当前起始位置
obj.LoadFromFile("", LJ); // 装载指定文件内容
buffer := "";
SetLength(buffer, 10);
obj.position := 0;
obj.read(buffer, 10); // 读取前10个节字的内容到buffer中
return buffer;
```

返回：Tinysoft h

其中，文件 TestAAA.txt 的内容为：

###### LoadFromStream

范例：

```tsl
obj := CreateObject("TMemoryStream");
buffer := "ABC-123";
obj.write(buffer, 8);
obj2 := CreateObject("TMemoryStream");
cf := obj2.LoadFromStream(obj); // 将obj加载到obj2中
buffer := "";
SetLength(buffer, 8);
obj2.position := 0;
obj2.read(buffer, 8);
return buffer;
```

返回："ABC-123\0"

###### SaveToFile

范例：将流中的内容存贮到指定的 txt 文件中

```tsl
LJ := "E:\\test\\TestBBB.txt";
obj := CreateObject("TMemoryStream");
obj.position := 0; // 设置当前起始位置
buffer := "ABC-123";
obj.write(buffer, 8);
obj.savetoFile("", LJ); // 保存
```

运行后，本地的 TestBBB.txt 文件如下：

###### SaveToStream

范例：将实例 obj2 的内容保存到实例 obj 中去

```tsl
obj := CreateObject("TMemoryStream");
buffer1 := "xxxxxxxx";
obj.write(buffer1, 8);
obj2 := CreateObject("TMemoryStream");
buffer := "ABC-123";
obj2.write(buffer, 7);
// obj.position:=0; // 改变保存的起始位置
cf := obj2.SaveToStream(obj); // 将obj2保存到obj中
buffer := "";
SetLength(buffer, 15);
obj.position := 0;
obj.read(buffer, 15);
return buffer;
```

返回："xxxxxxxxABC-123"

#### THandleStream 对象

参见：TStream 对象

##### THandleStream 对象的创建

- THandleStream 对象的创建

#### TFileStream 对象

参见：TStream 对象

##### TFileStream 对象的创建

范例：

```tsl
FileName := "E:\\Test\\TFileCase01.txt";
// -新建一个txt文件并打开
obj := CreateObject("TFileStream", "", FileName, 65535);
buffer := "ABC 123";
p := obj.write(buffer, 7); // 写入
// -读取
b := "";
SetLength(b, 7);
obj.Position := 0; // 必须设置起始位置，否则失败
obj.Read(b, 7);
return b;
```

返回字符串：ABC 123

#### TIniFile 对象

##### TIniFile 的创建

调用：CreateObject('TIniFile',Alias,FileName:string)

参数：

Alias：字符串类型，目录别名。

FileName：字符串类型，INI 文件名。

##### TIniFile 的方法

###### SectionExists

###### ReadString

###### WriteString

###### ReadInteger

###### WriteInteger

###### ReadBoolean

###### WriteBoolean

###### ReadBinaryStream

###### WriteBinaryStream

###### ReadDate

###### WriteDate

###### ReadDateTime

###### WriteDateTime

###### ReadFloat

###### WriteFloat

###### ReadSection

###### ReadSectionValues

###### ReadSections

###### EraseSection

###### DeleteKey

###### ValueExists

#### TMemIniFile 对象

参考：TIniFile 对象，TIniFile 的方法对 TmemIniFile 同样适用。

##### TMemIniFile 的创建

范例

```tsl
// 创建TMemIniFile对象
obj := CreateObject("TMemIniFile");
// 设置内容
sLobj := new tstringlist();
sLobj.LoadFromFile("", "D:\\Test\\File.ini");
obj.SetStrings(sLobj);
// 进行其他操作
...
```

##### TMemIniFile 的方法

###### Clear

范例

ini 文件内容如下：

[Test]

string=TinySoft

[ReadStream]

array=0a021f35

string=4265376941725678666a3871

```tsl
// 创建TMemIniFile对象
obj := CreateObject("TMemIniFile");
// 设置内容
sLobj := new tstringlist();
sLobj.LoadFromFile("", "D:\\Test\\File.ini");
obj.SetStrings(sLobj);
// 清空设置的内容
obj.clear();
// 获取设置好的内容
getObj := new TStringList();
obj.GetStrings(getObj);
return getObj.GetText();
```

结果：""

###### GetStrings

范例

ini 文件内容如下：

[Test]

string=TinySoft

[ReadStream]

array=0a021f35

string=4265376941725678666a3871

```tsl
// 创建TMemIniFile对象
obj := CreateObject("TMemIniFile");
// 设置内容
sLobj := new tstringlist();
sLobj.LoadFromFile("", "D:\\Test\\File.ini");
obj.SetStrings(sLobj);
// 获取设置好的内容
getObj := new TStringList();
obj.GetStrings(getObj);
return getObj.GetText();
```

###### SetStrings

范例

ini 文件内容如下：

[Test]

string=TinySoft

[ReadStream]

array=0a021f35

string=4265376941725678666a3871

```tsl
// 创建TMemIniFile对象
obj := CreateObject("TMemIniFile");
// 设置内容
sLobj := new tstringlist();
sLobj.LoadFromFile("", "D:\\Test\\File.ini");
obj.SetStrings(sLobj);
// 获取设置好的内容
getObj := new TStringList();
obj.GetStrings(getObj);
return getObj.GetText();
```

##### TMemIniFile 的属性

###### CaseSensitive

范例

ini 文件内容如下：

[Test]

string=TinySoft

[ReadStream]

array=0a021f35

string=4265376941725678666a3871

```tsl
// 创建TMemIniFile对象
obj := CreateObject("TMemIniFile");
// 设置大小写相关
obj.CaseSensitive := 1;
// 设置内容
sLobj := new tstringlist();
sLobj.LoadFromFile("", "D:\\Test\\File.ini");
obj.SetStrings(sLobj);
// 获取设置好的内容
t1 := obj.ReadString("Test", "string", ""); // 大小写一致
t2 := obj.ReadString("test", "string", ""); // 大小写不一致
return array(t1, t2);
```

#### TRegistryIniFile 对象

参考：TIniFile 对象

##### TRegistryIniFile 的创建

- TRegistryIniFile 的创建

#### TWebResponse 对象

TWebResponse 是对 Delphi 的兼容对象，TSL 语言本身提供了函数化的输出以及头设置的方法，但是用 TWebResponse 可以方便熟悉 Delphi 开发的人员。

##### TWebResponse 的创建

调用：CreateObject('TWebResponse')

##### TWebResponse 的属性

###### Allow

###### ContentStream

###### ContentEncoding

###### ContentLength

###### ContentType

###### ContentVersion

###### Cookies

参考 TCookieCollection 对象

###### CustomHeaders

###### Date

###### DerivedFrom

###### Expires

###### HttpRequest

###### LastModified

###### Location

###### LogMessage

###### Realm

###### ReasonString

###### Server

###### StatusCode

###### Title

###### Version

- Allow
- ContentStream
- ContentEncoding
- ContentLength
- ContentType
- ContentVersion
- Cookies
- CustomHeaders
- Date
- DerivedFrom
- Expires
- HttpRequest
- LastModified
- Location
- LogMessage
- Realm
- ReasonString
- Server
- StatusCode
- Title
- Version
- WWWAuthenticate

###### WWWAuthenticate

##### TWebResponse 的方法

###### GetCustomHeader

###### SendRedirect

###### SendResponse

###### SendStream

###### Sent

###### SetCookieField

###### SetCustomHeader

#### TWebRequest 对象

TWebRequest 是对 Delphi 的兼容对象，TSL 语言本身提供了函数化的获得请求信息的方法，但是用 TWebRequest 可以方便熟悉 Delphi 开发的人员。

##### TWebRequest 的创建

调用：CreateObject('TWebRequest')

##### TWebRequest 的属性

###### Accept

###### Authorization

###### CacheControl

###### Connection

###### Content

###### ContentEncoding

###### ContentFields

###### ContentLength

###### ContentType

###### ContentVersion

###### Cookie

###### CookieFields

###### Date

###### DerivedFrom

###### Expires

###### From

###### Host

###### IfModifiedSince

###### InternalPathInfo

###### InternalScriptName

###### Method

###### MethodType

###### PathInfo

- Accept
- Authorization
- CacheControl
- Connection
- Content
- ContentEncoding
- ContentFields
- ContentLength
- ContentType
- ContentVersion
- Cookie
- CookieFields
- Date
- DerivedFrom
- Expires
- From
- Host
- IfModifiedSince
- InternalPathInfo
- InternalScriptName
- Method
- MethodType
- PathInfo
- PathTranslated
- ProtocolVersion
- Query
- QueryFields
- Referer
- RemoteAddr
- RemoteHost
- ScriptName
- ServerPort
- Title
- Url
- UserAgent

###### PathTranslated

###### ProtocolVersion

###### Query

###### QueryFields

###### Referer

###### RemoteAddr

###### RemoteHost

###### ScriptName

- Accept
- Authorization
- CacheControl
- Connection
- Content
- ContentEncoding
- ContentFields
- ContentLength
- ContentType
- ContentVersion
- Cookie
- CookieFields
- Date
- DerivedFrom
- Expires
- From
- Host
- IfModifiedSince
- InternalPathInfo
- InternalScriptName
- Method
- MethodType
- PathInfo
- PathTranslated
- ProtocolVersion
- Query
- QueryFields
- Referer
- RemoteAddr
- RemoteHost
- ScriptName
- ServerPort
- Title
- Url
- UserAgent

###### ServerPort

###### Title

###### Url

###### UserAgent

##### TWebRequest 的方法

###### ReadClient

###### WriteClient

###### ReadString

###### WriteString

###### WriteHeaders

###### ExtractContentFields

###### ExtractCookieFields

###### ExtractQueryFields

###### GetFieldByName

###### TranslateURI

#### TCookieCollection 对象

TCookieCollection 不能直接创建，该对象从 TWebResponse 实例中通过 Cookies 属性获得。

参见：TWebResponse 对象，TCookie 对象

##### TCookieCollection 的方法

###### Add

参考 TCookie 对象

###### Clear

##### TCookieCollection 的属性

###### Count

###### Items

参考 TCookie 对象

###### WebResponse

参考 TWebResponse 对象

#### TCookie 对象

##### TCookie 的属性

###### Domain

###### Expires

###### Name

###### Path

###### Secure

###### Value

##### TCookie 的方法

###### HeaderValue

###### Assign

###### AssignTo

#### TSessionMan 对象

Session 管理器，可以用来在 WEB 服务器端存贮信息，例如存贮用户的登录状态。

参见：TSession 对象

##### TSessionMan 的创建

```tsl
TSessionMan 对象的创建方法：CreateObject("TSessionMan");
```

##### TSessionMan 的方法

###### NewSession

参考 TSession 对象

###### GetSession

参考 TSession 对象

###### DeleteSession

参考 TSession 对象

###### OnlineUser

###### OnlineSession

#### TSession 对象

TSession 对象用于管理一个会话的数据存贮，由 TSessionMan 对象来创建，用来在 WEB 端存贮用户的信息。TSession 对象由 TMemIniFile 派生而来，所以 TMemIniFile 的方法和属性 TSession 也都支持。

参见：TSessionMan 对象，TMemIniFile 对象

##### TSession 的属性

参见：TMemIniFile 对象

###### LastActive

###### Refs

###### LiveSeconds

###### UserId

###### SessionId

#### SMTP 对象

说明：邮件发送对象,可派出出子类。

##### SMTP 对象的创建

范例

```tsl
// 范例1：创建smtp对象
Obj := CreateObject("SMTP");
// 范例2：根据配置创建smtp对象，配置写在\tinysoft\analyse.net\plugin\fileMgr.ini 文件
{ [Smtp Settings]
test:Port = 25
test:UserName = name
test:Password = password
test := smtp.tinysoft.com.cn
}
obj := CreateObject("smtp", "test");
echo obj.Authenticate(); // 验证账号密码是否正确。
return 1;
```

##### SMTP 对象的方法

###### Login

###### SendCmd

范例

```tsl
// 发送HELO命令。
obj := CreateObject("smtp");
obj.Host := 'smtp.tinysoft.com.cn';
obj.connect();
obj.sendcmd("HELO smtp.tinysoft.com.cn\r\n", r, c);
return r;
// 结果：665518 Hello smtp.tinysoft.com.cn [ip], pleased to meet you.
```

###### Connect

范例

```tsl
obj := CreateObject("smtp");
obj.Host := 'smtp.tinysoft.com.cn';
obj.connect();
return 1;
```

###### Disconnect

范例

```tsl
obj := CreateObject("smtp");
obj.Host := 'smtp.tinysoft.com.cn';
obj.connect();
obj.disconnect();
return 1;
```

###### DisconnectNotifyPeer

范例

```tsl
obj := CreateObject("smtp");
obj.Host := 'smtp.tinysoft.com.cn';
obj.connect();
obj.DisconnectNotifyPeer();
return 1;
```

###### Send

范例

```tsl
obj := CreateObject("smtp");
obj.UserName := 'username'; // 邮箱账号
obj.Password := 'password'; // 邮箱密码
obj.Host := 'smtp.tinysoft.com.cn'; // smtp地址
obj.port := 25; // 端口
obj.connect();
// 设置mailmsg对象
msg := CreateObject("MailMsg");
msg.subject := "SendMailTest"; // 邮件标题
msg.body := "test"; // 邮件内容
msg.Sender := "username@tinysoft.com.cn"; // 邮件发送人邮箱
msg.Recipients := " Recipients @qq.com"; // 邮件接收人邮箱
try
    obj.send(msg);
except
    echo '\r\n邮件错误信息', obj.LastCmdResult();
    return echo '\r\n邮件发送失败:', ExceptObject.errinfo;
end;
return 1;
```

参考 MailMsg 对象

###### Authenticate

范例

```tsl
obj := CreateObject("smtp");
obj.UserName := 'username'; // 邮箱账号
obj.Password := 'password'; // 密码
obj.Host := 'smtp.tinysoft.com.cn';
obj.port := 25;
obj.connect();
return obj.Authenticate(); // 结果：1
```

###### QuickSend

范例

```tsl
obj := CreateObject("smtp");
obj.UserName := 'username'; // 邮箱账号
obj.Password := 'password'; // 邮箱密码
obj.Host := 'smtp.tinysoft.com.cn'; // smtp地址
obj.port := 25; // 端口
obj.connect();
try
    mailHost := 'smtp.tinysoft.com.cn'; // 邮件服务器地址
    mailSubject := "SendMailTest"; // 邮件标题
    mailBody := "test"; // 邮件内容
    mailSender := "username@tinysoft.com.cn"; // 邮件发送人邮箱
    mailRecipients := " Recipients@qq.com"; // 邮件接收人邮箱
    obj.QuickSend(mailHost, mailSubject, mailRecipients, mailSender, mailBody);
except
    echo '\r\n邮件错误信息', obj.LastCmdResult();
    return echo '\r\n邮件发送失败:', ExceptObject.errinfo;
end;
return 1;
```

###### Expand

###### Verify

范例

```tsl
obj := CreateObject("smtp", "test") ;
obj.Host := 'smtp.tinysoft.com.cn';
obj.port := 25;
try
    username := "username@tinysoft.com.cn";
    return obj.verify(username);
except
return echo ExceptObject.errinfo;
end;
//结果：username <username@tinysoft.com.cn>
```

##### SMTP 对象属性

###### AuthType

###### Host

###### Port

###### Username

###### Password

###### UseTLS

###### LastCmdResult

###### LastCmdResultCode

###### DidAuthenticate

#### Pop3 对象

说明：Pop3 邮件处理对象。可派出出子类

##### Pop3 对象的创建

范例

```tsl
// 范例1：创建 pop3 对象
pop3 := CreateObject("POP3");

// 范例2：根据配置创建 pop3 对象并自动连接登录，配置写在 \\tinysoft\\analyse.net\\plugin\\fileMgr.ini 文件
{
[class:POP3]
permit := local
[POP3 Settings]
test:Port=110
test:UserName=name
test:Password=password
test := pop3.tinysoft.com.cn
}
pop3 := CreateObject("POP3", "test", "name", "password");
echo pop3.CheckMessages(); // 若创建成功，则打印邮件数量
return 1;
```

##### Pop3 对象的方法

使用范例：

```tsl
ret := CreateObject('PoP3', 'POP3.TINYSOFT.COM.CN', uName, passWord);
ret.UIDL(ids); // 取服务器的邮件唯一标示列表存放在ids中
s := str2array(ids, '\r\n');
ss := array();
for i := 0 to length(s) - 2 do ss[i] := str2array(s[i], ' ');
ss[:, 0] := strtoint(ss[:, 0]);
MsgID := ss[3, 0]; // 以第三个邮件为例
ret.Top(MsgID, r, 10); // 指定邮件内容前N行
echo '取邮件的前N行内容:', r[length(r) - 40:];
d := ret.RetrieveMsgSize(MsgID); // 邮件大小
echo '邮件大小:', d;
d1 := ret.RetrieveMailBoxSize(); // 邮箱大小
echo '邮箱大小:', d1;
r1 := ret.Retrieve(MsgID, lr); // 返回结果为MailMSg类型
echo '取邮件内容:', r1, ' ', lr;
echo tostn(return MailMsg(lr));
r2 := ret.RetrieveHeader(MsgID, hr); // 返回结果为MailMSg类型
echo '取邮件头部信息:', r2, ' ', hr;
echo tostn(return MailMsg(hr));
r3 := ret.RetrieveHeader(MsgID, yr); // 返回结果为MailMSg类型
echo '取邮件的原始内容:', r3, ' ', yr;
echo tostn(return MailMsg(yr));
ss := ret.CAPA();
echo '取服务器的支持命令列表:', ss;
return ss;
```

打印的信息截图：（只展示部分信息）

###### Login

###### SendCmd

范例

```tsl
ret := CreateObject('PoP3', 'POP3.TINYSOFT.COM.CN', uName, password);
r := ret.SendCmd('UIDL 1', cr, rc); // 获取指定邮件的唯一标识
return array(r, cr, rc);
```

返回:

###### Connect

范例

```tsl
ret := CreateObject('Pop3');
// 通过属性设置后再连接
ret.Username := uName;
ret.Password := passWord;
ret.Host := 'POP3.TINYSOFT.COM.CN';
ret.Port := 110;
ret.UseTLS := 0;
ret.Connect(); // 连接邮箱服务器
return ret.CheckMessages();
```

返回:

406

###### Disconnect

###### DisconnectNotifyPeer

###### CheckMessages

范例

```tsl
// 登陆并返回邮箱中邮件数量
ret := CreateObject('PoP3', 'POP3.TINYSOFT.COM.CN', uname, password);
return ret.CheckMessages(); // 返回整数
```

###### KeepAlive

###### Reset

###### Delete

###### Top

范例

获取指定邮件前 3 行内容

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
ret := pop3Obj.Top(1, msg, 3);
if ret then return msg;
else return "获取指定邮件前3行内容失败！";
```

###### RetrieveMsgSize

范例

获取指定邮件大小

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
return pop3Obj.RetrieveMsgSize(1);
```

结果：18

###### RetrieveMailBoxSize

范例

获取邮箱大小

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
return pop3Obj.RetrieveMailBoxSize();
```

###### Retrieve

范例 01：获取指定邮件内容

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
ret := pop3Obj.Retrieve(1, msg);
if ret then return msg.asstring;
else return "获取指定邮件内容失败！";
```

###### RetrieveHeader

范例 01：获取指定邮件头部信息

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
ret := pop3Obj.RetrieveHeader(1, msg);
if ret then return msg.asstring;
else return "获取指定邮件内容失败！";
```

###### RetrieveRaw

范例 01：获取指定邮件的原始内容

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
ret := pop3Obj.RetrieveRaw(1, msg);
if ret then return msg;
else return "获取指定邮件内容失败！";
```

###### UIDL

范例

```tsl
ret := CreateObject('PoP3', 'POP3.TINYSOFT.COM.CN', uname, password);
ret.UIDL(ids); // 取服务器的邮件唯一标示列表存放在ids中
return ids;
```

返回:

（部分结果）

1 20220715095212230A

2 20220715142439001C

3 20220715151305015D

4 20220715182243051E

5 202207180708290CF7

6 20220718094652103B

7 20220718100236108E

8 2022071810050110AE

…

###### CAPA

范例

获取邮件支持的所有命令

```tsl
// 创建pop3对象
pop3Obj := new pop3("pop3.tinysoft.com.cn", "username", "password");
return := pop3Obj.CAPA();
```

##### Pop3 对象属性

###### AuthType

###### Host

###### Port

###### Username

###### Password

###### UseTLS

###### LastCmdResult

###### LastCmdResultCode

###### Capabilities

###### HasCAPA

###### HasAPOP

###### AutoLogin

#### MailMsg 对象

邮件消息对象，用来 POP3 对象接收邮件或者 SMTP 对象发送邮件。辅助类，不能派出子类。

##### MailMsg 对象的创建

调用：CreateObject('MailMsg')

##### MailMsg 对象的方法

###### AddText

参考 MessagePart 对象

###### SendCmd

###### SaveToFile

###### LoadFromFile

###### SaveToStream

###### LoadFromStream

###### MessagePart

参考 MessagePart 对象

###### AddAttachment

参考 MessagePart 对象

##### MailMsg 对象属性

###### Flags

###### IsEncoded

###### MsgID

###### Headers

###### UID

###### IsMsgSinglePartMime

###### AttachmentEncoding

###### Body

###### BccList

###### CCList

- Flags
- IsEncoded
- MsgID
- Headers
- UID
- IsMsgSinglePartMime
- AttachmentEncoding
- Body
- BccList
- CCList
- CharSet
- ContentType
- ContentTransferEncoding
- ContentDisposition
- Date
- Encoding
- ExtraHeaders
- FromList
- From
- NewsGroups
- NoEncode
- NoDecode
- Organization
- Priority
- ReceiptRecipient
- Recipients
- References
- InReplyTo
- ReplyTo
- Subject
- Sender
- UseNowForDate
- LastGeneratedHeaders
- ConvertPreamble
- ExceptionOnBlockedAttachments
- AttachmentTempDirectory
- MessagePartsCount
- AsString
- AsBinary

###### CharSet

###### ContentType

###### ContentTransferEncoding

###### ContentDisposition

###### Date

###### Encoding

- Flags
- IsEncoded
- MsgID
- Headers
- UID
- IsMsgSinglePartMime
- AttachmentEncoding
- Body
- BccList
- CCList
- CharSet
- ContentType
- ContentTransferEncoding
- ContentDisposition
- Date
- Encoding
- ExtraHeaders
- FromList
- From
- NewsGroups
- NoEncode
- NoDecode
- Organization
- Priority
- ReceiptRecipient
- Recipients
- References
- InReplyTo
- ReplyTo
- Subject
- Sender
- UseNowForDate
- LastGeneratedHeaders
- ConvertPreamble
- ExceptionOnBlockedAttachments
- AttachmentTempDirectory
- MessagePartsCount
- AsString
- AsBinary

###### ExtraHeaders

###### FromList

###### From

- Flags
- IsEncoded
- MsgID
- Headers
- UID
- IsMsgSinglePartMime
- AttachmentEncoding
- Body
- BccList
- CCList
- CharSet
- ContentType
- ContentTransferEncoding
- ContentDisposition
- Date
- Encoding
- ExtraHeaders
- FromList
- From
- NewsGroups
- NoEncode
- NoDecode
- Organization
- Priority
- ReceiptRecipient
- Recipients
- References
- InReplyTo
- ReplyTo
- Subject
- Sender
- UseNowForDate
- LastGeneratedHeaders
- ConvertPreamble
- ExceptionOnBlockedAttachments
- AttachmentTempDirectory
- MessagePartsCount
- AsString
- AsBinary

###### NewsGroups

###### NoEncode

###### NoDecode

###### Organization

###### Priority

###### ReceiptRecipient

###### Recipients

###### References

###### InReplyTo

###### ReplyTo

- Flags
- IsEncoded
- MsgID
- Headers
- UID
- IsMsgSinglePartMime
- AttachmentEncoding
- Body
- BccList
- CCList
- CharSet
- ContentType
- ContentTransferEncoding
- ContentDisposition
- Date
- Encoding
- ExtraHeaders
- FromList
- From
- NewsGroups
- NoEncode
- NoDecode
- Organization
- Priority
- ReceiptRecipient
- Recipients
- References
- InReplyTo
- ReplyTo
- Subject
- Sender
- UseNowForDate
- LastGeneratedHeaders
- ConvertPreamble
- ExceptionOnBlockedAttachments
- AttachmentTempDirectory
- MessagePartsCount
- AsString
- AsBinary

###### Subject

###### Sender

###### UseNowForDate

###### LastGeneratedHeaders

###### ConvertPreamble

###### ExceptionOnBlockedAttachments

###### AttachmentTempDirectory

###### MessagePartsCount

###### AsString

###### AsBinary

#### MessagePart 对象

MesssagePart 是从属于 MailMsg 对象的消息部分。辅助类，不能派出子类。

##### MessagePart 对象的创建

调用：CreateObject('MessagePart')。

注：一般 MessagePart 无需主动创建，而是由 POP3 或者 NNTP 对象接收的 MailMsg 对象里自动创建，或者在 MailMsg 里调用 AddAttachment、AddText 方法来产生。

##### MessagePart 对象的方法

###### SaveToFile

###### LoadFromFile

###### SaveToStream

###### LoadFromStream

##### MessagePart 对象属性

###### ContentType

###### IsEncoded

###### MsgID

###### Headers

###### CharSet

###### ExtraHeaders

###### ContentTransfer

###### ContentID

###### ContentDescription

###### ContentLocation

###### ParentPart

###### PartType

###### FileName

###### Body

###### AsString

###### AsBinary
