# 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 ( [, 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", 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 ``` ##### 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