# TSL 对象与类 文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;[02_core_model.md](02_core_model.md)(优先)、[09_units_and_scope.md](09_units_and_scope.md)、[20_object_runtime_and_introspection.md](20_object_runtime_and_introspection.md)、[21_builtin_runtime_objects.md](21_builtin_runtime_objects.md)、[24_object_overloads_and_iteration.md](24_object_overloads_and_iteration.md)、[11_pitfalls.md](11_pitfalls.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md) 这一篇收拢面向对象语法,只保留语言层规则。 ## 本篇职责 回答“`type Name = class`、字段、`static`、方法、`property`、析构、类类型、继承和对象创建在 TSL 里怎样写”。 本页后半段有少量依赖 `unit` 的双文件示例;如果你还没建立 `unit` / `uses` 的多文件心智模型,先看 [09_units_and_scope.md](09_units_and_scope.md)。 ## 智能体对象/类判断流程 1. 先判断要写普通对象、继承、构造函数、类方法、静态字段还是运行时创建。 2. 对象声明优先使用本页明确的 `type Name = class ... end;` 骨架。 3. 普通本地类创建默认用 `new ClassName()`;只有需要字符串类名、类类型变量或跨 `unit` 路径时,才用 `createObject(...)`。 4. 类方法和静态字段优先用 `class(Name).Member` 或文档明确反射入口,不要裸写类名调用。 5. 构造函数默认保持 `public create`,不要把 `private` / `protected create` 当成会自动执行的构造函数。 6. 普通类成员默认显式写 `public` 段,不依赖隐式 public。 7. 方法体内访问当前实例成员默认直接写成员名;成员读写为了性能不加 `self` 前缀。 8. 命中对象反射 / 运行时状态、内置运行时对象、对象重载 / 迭代时,分别跳到 [20_object_runtime_and_introspection.md](20_object_runtime_and_introspection.md)、[21_builtin_runtime_objects.md](21_builtin_runtime_objects.md)、[24_object_overloads_and_iteration.md](24_object_overloads_and_iteration.md)。 9. 没有文档事实时不要发明对象/类写法。 ## 核心规则 - 类定义统一按 `type Name = class ... end;` 写。 - 顶层类声明可以放在松散语句之后(单向允许)。 - 顶层类声明也可以放在顶层 `function / procedure` 之后。 - 类声明不要写在函数体内部。 - 在松散语句脚本里,可以先写松散语句再进入顶层类声明;但一旦进入顶层 `type Name = class ... end;` 声明,后面不能再回到松散语句模式。 - 类里可以直接写字段和方法。 - 字段可以写类型注解,例如 `name_: string;`。 - 类方法参数和返回值可以写类型注解,例如 `function ReadName(): string;`。 - 类的声明和实现可以分离:类内可以只声明方法签名,再用 `function ClassName.Method(...)` 在类外实现;带类型和 `overload` 时,类外实现保持同一组签名;类外实现属于声明区,后面不要再追加脚本语句。 - `static` 字段可用;可以通过类类型访问,也可以通过实例访问。 - `const` 成员可用;实例常量可通过实例读取,`static const` 可通过类类型读取,也可用于成员函数默认参数。 - 方法体里默认直接用成员名访问当前实例成员;普通成员读写为了性能不加 `self` 前缀,也不要为了“显式”访问而补这个前缀。 - `overload` 方法可用;同名不同参数个数的方法可以共存。 - 基础 `property` 形态:`property Name read fieldOrMethod write fieldOrMethod`。 - `property Name: Type ...` 这种类型注解写法可用。 - 参数化 `property` 可用;本页只写基础模式。 - 参数化 `property` 的 accessor 方法记录两种常见模式:读方法接同参数个数,写方法接“参数个数 + 赋值值”。 - 索引型 `property` 可用;调用时用圆括号 `obj.Prop(index)`。 - 固定 `index` property 可用;它可以把某个固定索引直接映射成普通属性读写。 - `class(Name)` 和 `findClass("Name")` 都可以拿到类类型。 - `findClass("ClassName", obj)` 可把对象转成指定父类视图。 - `class function` 可用;本页里的“类方法”就是指 `class function`,前者是中文描述,后者是代码写法。 - 类方法默认先拿到类类型,再在类类型上调用。 - 基础继承写法是 `type Child = class(Parent)`。 - 多重继承写法 `type Child = class(Base1, Base2)` 可用。 - 基础覆盖写法是:父类方法声明为 `virtual`,子类对应方法声明为 `override`。 - 基础祖先类调用:可以用 `Inherited;`、`Inherited MethodName(...)` 或 `class(BaseClass, ObjectName).MethodName()`。 - 创建对象有两种方式:`new ClassName()` 最常用,`createObject(...)` 作为次选。 - 普通本地类实例化默认生成 `new ClassName()`;`createObject("ClassName")`、`createObject(ClassType)` 只在字符串类名、类类型变量或跨 `unit` 路径场景生成。 - 如果类里定义了 `function create(...)`,`new`、`createObject("ClassName", ...)` 和 `createObject(ClassType, ...)` 都可以透传构造参数,也都支持默认参数和命名参数。 - 析构写法是无参 `function destroy();`;把对象引用设为 `nil` 时会触发它。 - 工厂式 `self(0)` / `self(1)` 可用;不要生成 `self()` 这种无参工厂式写法。 - 跨 `unit` 类路径创建和继承属于多文件边界;先回看 [09_units_and_scope.md](09_units_and_scope.md) 和 [19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md),不要从普通单文件 `new` / `createObject` 规则直接泛化。 - `{$ifdef parentClassInUnit}` 为真,可用于探测“继承和构造单元中的类”能力是否可用。 - `private` / `protected` / `public` 可用;类开头未写可见性时,成员默认按 `public` 处理,但生成代码默认显式写 `public`。 - 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。 - 外部访问 `private` / `protected` 字段或方法,会在执行时报对象成员访问错误。 - 子类可以直接访问父类 `protected` 成员;不能直接访问父类 `private` 成员。 - `create` 写成 `private` 或 `protected` 时,对象仍能创建,但 `new` 和 `createObject(...)` 不会执行该构造逻辑;构造函数应保持 `public`。 - 更复杂的多重继承边界和更复杂的对象模型不在本页展开。 ## 可直接照写示例 使用这些示例时遵守: - 普通本地类创建优先复制 `new ClassName()` 形态。 - `createObject(...)` 示例只在字符串类名、类类型变量或跨 `unit` 路径场景复制。 - `property` 类型注解只有在已有类型名证据时生成;不要为了完整性发明说明性类型名。 ### 最小类与声明位置 最短类骨架: 如果任务只是要“先写出一个类”,先从这个骨架起手;后面的例子再逐步进入字段、工厂函数和声明位置边界。 代码块身份:可直接照写示例 ```tsl type Person = class end; ``` 字段、方法和 `new`: 代码块身份:可直接照写示例 ```tsl type Person = class public name; function SetName(new_name); begin name := new_name; end; end; function MakePerson(); begin return new Person(); end; ``` 类声明位置: 代码块身份:可直接照写示例 ```tsl obj := new MyClass(); obj.value := 5; writeLn(obj.value); type MyClass = class public value; end; ``` 输出说明: - 输出 `5` - 说明顶层类声明可以放在松散语句之后 - 同时也说明 `new MyClass()` 可以解析到后面才出现的类声明 代码块身份:输出片段 ```text 5 ``` 顶层函数后面也可以继续声明类: 代码块身份:可直接照写示例 ```tsl function MakeObj(); begin return 1; end; type MyClass = class public value; end; ``` 结果说明: - 上述声明顺序可以编译通过 类声明位置的反例: 代码块身份:反例 / 不可照写 ```text // 函数体内部声明类 function Demo(); begin type InnerClass = class value; end; return 1; end; // 松散语句在 type 之后继续出现 a := 1; type MyClass = class public value; end; writeLn(a); ``` 结果说明: - 函数体内部声明类会报 `invalid statement` - 在松散语句脚本里,`type MyClass = class ... end;` 之后继续写 `writeLn(a);` 也会报 `invalid statement` ### 字段、静态成员、常量与可见性 `static` 字段: 代码块身份:可直接照写示例 ```tsl class(THuman).mCount := 100; writeLn(class(THuman).mCount); h := new THuman(); writeLn(class(THuman).mCount); writeLn(h.mCount); type THuman = class public static mCount; function create(); begin mCount := (mCount ?: 0) + 1; end; end; ``` 输出说明: - `class(THuman).mCount := 100` 可以直接写静态字段 - `class(THuman).mCount` 先输出 `100` - 创建对象后,`class(THuman).mCount` 输出 `101` - 通过实例读取 `h.mCount` 也输出 `101` - 单个静态字段默认写成 `static mCount;` `const` 成员: 代码块身份:可直接照写示例 ```tsl o := new C(); writeLn(o.TestConst()); writeLn(o.TestConstInParam()); writeLn(o.mA); writeLn(class(C).mB); type C = class public const mA = 1; static const mB = mA + 10; function TestConst(); begin return mA + mB; end; function TestConstInParam(b = mB); begin return b; end; end; ``` 输出说明: - 依次输出 `12`、`11`、`1`、`11` - 说明类里可以定义实例常量和 `static const` - 说明实例常量可通过实例读取,`static const` 可通过 `class(C)` 读取 - 说明成员常量也可用于成员函数默认参数 成员方法内直接访问成员: 代码块身份:可直接照写示例 ```tsl c := new Counter(10); writeLn(c.Inc()); type Counter = class public value; function create(v); begin value := v; end; function Inc(); begin value := value + 1; return value; end; end; ``` 输出说明: - `c.Inc()` 输出 `11` 可见性 `private` / `protected` / `public`: 代码块身份:可直接照写示例 ```tsl a := new A(); a.value := 9; writeLn(a.value); writeLn(a.ReadSecret()); type A = class public value; private secret; backup; public function create(); begin secret := 2; backup := 3; end; function ReadSecret(); begin return secret + backup; end; end; ``` 输出说明: - `value` 写在显式 `public` 段里,因此 `a.value := 9` 可以直接写。 - `backup` 跟在 `private` 段后面,没有重新切换关键字,因此仍按 `private` 处理。 - 上述例子依次输出 `9`、`5`。 `protected` 可供子类直接访问: 代码块身份:可直接照写示例 ```tsl b := new B(); b.SetValue(66); writeLn(b.GetValue()); writeLn(b.TryCall()); type A = class protected value; function Hidden(); begin return 88; end; end; type B = class(A) public function SetValue(v); begin value := v; end; function GetValue(); begin return value; end; function TryCall(); begin return Hidden(); end; end; ``` 输出说明: - 子类里可以直接访问父类 `protected` 字段。 - 子类里也可以直接调用父类 `protected` 方法。 - 上述例子依次输出 `66`、`88`。 `private` 的失败场景,这里用 `text` 展示反例: 代码块身份:反例 / 不可照写 ```text // 外部访问 private 字段 a := new A(); writeLn(a.secret); type A = class private secret; public function create(); begin secret := 7; end; end; // 子类调用父类 private 方法 b := new B(); writeLn(b.TryCall()); type A = class private function Hidden(); begin return 77; end; end; type B = class(A) public function TryCall(); begin return Hidden(); end; end; ``` 输出说明: - 外部访问 `a.secret` 会在执行时报对象成员访问错误。 - 子类里直接调用父类 `private` 方法也会在执行时报错。 - `private` / `protected` 方法访问也遵循同样边界:外部不能调 `private` / `protected` 方法,子类只能调 `protected` 方法,不能调 `private` 方法。 ### 构造函数边界 `create` 建议保持 `public`: 代码块身份:可直接照写示例 ```tsl a := new A(111); writeLn(a.value); type A = class public value; private function create(v); begin value := v; end; end; ``` 输出说明: - `a := new A(111)` 可以创建对象。 - 上述例子里的 `a.value` 输出 ``,说明 `private create` 没有执行。 - `protected create`、`createObject("A", ...)` 和 `createObject(class(A), ...)` 也按同一规则处理;构造函数应保持 `public`。 ### 属性、类型注解与类外实现 基础 `property`: 代码块身份:可直接照写示例 ```tsl b := new MyBox(); b.Value := 7; writeLn(b.Value); b.Value := -1; writeLn(b.Value); type MyBox = class public _value; function SetValue(v); begin if v > 0 then _value := v; end; property Value read _value write SetValue; end; ``` 输出说明: - `b.Value := 7` 后,`b.Value` 输出 `7` - `b.Value := -1` 后,`b.Value` 仍输出 `7` 带类型注解的 `property`: 代码块身份:可直接照写示例 ```tsl b := new MyBox(); b.Value := 9; writeLn(b.Value); type MyBox = class public _value; function SetValue(v); begin _value := v; end; property Value: integer read _value write SetValue; end; ``` 输出说明: - `property Value: integer ...` 这种类型注解写法可以通过 - 上述例子中的 `b.Value` 输出 `9` 字段和类方法类型注解: 代码块身份:可直接照写示例 ```tsl box := new TypedBox("abc", 7); writeLn(box.Name); writeLn(box.Value); writeLn(box.ReadName()); type TypedBox = class public function create(_name: string; _value: any); begin name_ := _name; value_ := _value; end; function ReadName(): string; begin return name_; end; property Name: string read name_ write name_; property Value: any read value_ write value_; private name_: string; value_: any; end; ``` 输出说明: - `name_: string;` 和 `value_: any;` 可以作为类字段类型注解。 - `function create(_name: string; _value: any);` 可以作为类方法参数类型注解。 - `function ReadName(): string;` 可以作为类方法返回值类型注解。 - 上述例子依次输出 `abc`、`7`、`abc`。 代码块身份:输出片段 ```text abc 7 abc ``` 类内声明、类外实现的带类型重载方法: 代码块身份:可直接照写示例 ```tsl a := new PairBox("left"); writeLn(a.Left); writeLn(a.Right); b := new PairBox("left", 2); writeLn(b.Left); writeLn(b.Right); writeLn(b.ReadLeft()); type PairBox = class public function create(_left: string); overload; function create(_left: string; _right: any); overload; function ReadLeft(): string; property Left: string read left_ write left_; property Right: any read right_ write right_; private left_: string; right_: any; end; function PairBox.create(_left: string); overload; begin create(_left, nil); end; function PairBox.create(_left: string; _right: any); overload; begin left_ := _left; right_ := _right; end; function PairBox.ReadLeft(): string; begin return left_; end; ``` 输出说明: - 类内可以只声明带类型的重载方法签名。 - 类外实现写成 `function PairBox.create(...); overload;`,并保持同样的参数类型和 `overload` 标记。 - 类外实现属于声明区;写完后不要再追加脚本语句。 - 从一个构造函数转调另一个构造函数时,直接写 `create(_left, nil);`,不要加 `self` 前缀。 - 上述例子依次输出 `left`、``、`left`、`2`、`left`。 代码块身份:输出片段 ```text left left 2 left ``` 索引型 `property`: 代码块身份:可直接照写示例 ```tsl aa := new A(); aa.idx(0) := "abc"; writeLn(aa.idx(0)); type A = class public 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; ``` 输出说明: - `aa.idx(0) := "abc"` 可以写入索引 property - `aa.idx(0)` 输出 `abc` 固定 `index` property: 代码块身份:可直接照写示例 ```tsl aa := new A(); aa.idx0 := "abc"; writeLn(aa.idx0); writeLn(aa.idx(0)); type A = class public 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; property idx0 index 0 read rIndex write wIndex; end; ``` 输出说明: - `aa.idx0 := "abc"` 可以写入固定整数索引 property - `aa.idx0` 输出 `abc` - `aa.idx(0)` 也输出 `abc` 固定字符串索引: 代码块身份:可直接照写示例 ```tsl aa := new A(); aa.school := "math"; writeLn(aa.school); writeLn(aa.idx("High school")); type A = class public 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; property school index "High school" read rIndex write wIndex; end; ``` 输出说明: - `property school index "High school"` 这种固定字符串索引写法可以通过 - `aa.school` 输出 `math` - `aa.idx("High school")` 也输出 `math` 参数化 `property`: 代码块身份:可直接照写示例 ```tsl d := new MyDate(); d.DateV(2025, 8) := 10; writeLn(d.DateV()); type MyDate = class public _year; _month; _day; function getDateV(); begin return _year * 10000 + _month * 100 + _day; end; function setDateV(y, m, d); begin _year := y; _month := m; _day := d; end; property DateV(y, m) read getDateV write setDateV; end; ``` 输出说明: - `d.DateV(2025, 8) := 10` 可以给参数化 property 赋值 - `d.DateV()` 输出 `20250810` 参数化 `property` 的 accessor 方法参数: 代码块身份:可直接照写示例 ```tsl aa := new A(); aa.Item(2) := "x"; writeLn(aa.Item(2)); type A = class public arr; function create(); begin arr := array(); end; function getItem(i); begin return arr[i]; end; function setItem(i, value); begin arr[i] := value; end; property Item(i): string read getItem write setItem; end; ``` 输出说明: - `read getItem` 这种“读方法接同参数个数”的写法可以通过 - `write setItem` 这种“写方法接参数个数 + 赋值值”的写法可以通过 - 上述例子中的 `aa.Item(2)` 输出 `x` ### 对象创建与类类型 `new` 关键字: 代码块身份:可直接照写示例 ```tsl type Person = class end; function MakePerson(); begin return new Person(); end; ``` 通过类类型创建对象: 代码块身份:可直接照写示例 ```tsl cls := findClass("Person"); obj := createObject(cls); obj.value := 42; writeLn(obj.value); type Person = class public value; end; ``` 输出说明: - `createObject(cls)` 可以通过类类型创建对象 - 上述例子中的 `obj.value` 输出 `42` 也可以用 `class(Name)` 取得类类型: 代码块身份:可直接照写示例 ```tsl cls := class(Person); obj := createObject(cls, 44); writeLn(obj.value); type Person = class public value; function create(v); begin value := v; end; end; ``` 输出说明: - `class(Person)` 可以拿到 `Person` 的类类型 - `createObject(cls, 44)` 输出 `44` 构造参数、默认值和命名参数: 代码块身份:可直接照写示例 ```tsl a := new Person(22, c:99); writeLn(a.x); writeLn(a.y); writeLn(a.z); b := createObject("Person", 11); writeLn(b.x); writeLn(b.y); writeLn(b.z); type Person = class public x; y; z; function create(a, b = 20, c = 30); begin x := a; y := b; z := c; end; end; ``` 输出说明: - `new Person(22, c:99)` 依次输出 `22`、`20`、`99` - `createObject("Person", 11)` 依次输出 `11`、`20`、`30` - 说明构造函数默认参数和命名参数不只适用于普通函数调用,也适用于对象创建 类类型也同样支持默认参数和命名参数: 代码块身份:可直接照写示例 ```tsl cls := findClass("Person"); obj := createObject(cls, 33, c:77); writeLn(obj.x); writeLn(obj.y); writeLn(obj.z); type Person = class public x; y; z; function create(a, b = 20, c = 30); begin x := a; y := b; z := c; end; end; ``` 输出说明: - `createObject(cls, 33, c:77)` 依次输出 `33`、`20`、`77` - 因此类类型入口下的 `createObject(cls, ...)` 也遵循同样的构造参数规则 `class function`: 代码块身份:可直接照写示例 ```tsl cls1 := class(MathBox); writeLn(cls1.Add(3, 4)); cls2 := findClass("MathBox"); writeLn(cls2.Add(5, 6)); type MathBox = class public class function Add(x, y); begin return x + y; end; end; ``` 输出说明: - `class(MathBox).Add(...)` 可以调用类方法 - `findClass("MathBox").Add(...)` 可以调用类方法 - 上述例子依次输出 `7`、`11` ### 类方法、重载、继承与析构 `overload` 方法: 代码块身份:可直接照写示例 ```tsl t := new TestClass(); writeLn(t.fun(1, 2)); writeLn(t.fun(5)); type TestClass = class public function fun(p1, p2); overload; begin return p1 + p2; end; function fun(p1); overload; begin return p1 + 10; end; end; ``` 输出说明: - 依次输出 `3`、`15` - 说明同名不同参数个数的方法可以共存 基础继承: 代码块身份:可直接照写示例 ```tsl d := new Dog(); writeLn(d.Speak()); type Animal = class public function Speak(); begin return 1; end; end; type Dog = class(Animal) end; ``` 输出说明: - `type Dog = class(Animal)` 可以继承父类实例方法 - 上述例子中的 `d.Speak()` 输出 `1` 多重继承: 代码块身份:可直接照写示例 ```tsl obj := new C(); writeLn(obj.Fa()); writeLn(obj.Fb()); type A = class public function Fa(); begin return 1; end; end; type B = class public function Fb(); begin return 2; end; end; type C = class(A, B) end; ``` 输出说明: - `type C = class(A, B)` 可以同时继承 `A` 和 `B` 的方法 - 上述例子依次输出 `1`、`2` 多重继承下的同名方法优先级: 代码块身份:可直接照写示例 ```tsl obj := new C(); writeLn(obj.Speak()); type A = class public function Speak(); begin return 1; end; end; type B = class public function Speak(); begin return 2; end; end; type C = class(A, B) end; ``` 输出说明: - 当多个父类存在同名方法时,本例优先命中第一个父类 `A` - 上述例子中的 `obj.Speak()` 输出 `1` 基础 `virtual` / `override`: 代码块身份:可直接照写示例 ```tsl d := new Dog(); writeLn(d.Speak()); type Animal = class public function Speak(); virtual; begin return 1; end; end; type Dog = class(Animal) public function Speak(); override; begin return 2; end; end; ``` 输出说明: - 父类 `virtual` + 子类 `override` 组合可以通过 - 上述例子中的 `d.Speak()` 输出 `2` `class(BaseClass, ObjectName).MethodName()`: 代码块身份:可直接照写示例 ```tsl obj := new B(); writeLn(obj.Speak()); writeLn(class(A, obj).Speak()); type A = class public function Speak(); virtual; begin return 1; end; end; type B = class(A) public function Speak(); override; begin return 2; end; end; ``` 输出说明: - `obj.Speak()` 输出 `2` - `class(A, obj).Speak()` 会定向调用祖先类方法,因此输出 `1` `findClass("ClassName", obj)` 强制转型: 代码块身份:可直接照写示例 ```tsl objb := new ClassB(); obj := findClass("ClassA", objb); writeLn(obj.Fuc()); writeLn(obj is class(ClassA)); writeLn(obj is class(ClassB)); type ClassA = class public function Fuc(); virtual; begin return 1; end; end; type ClassB = class(ClassA) public function Fuc(); override; begin return 2; end; end; ``` 输出说明: - `findClass("ClassA", objb)` 会把 `objb` 转成 `ClassA` 视图 - 上述例子中的 `obj.Fuc()` 输出 `1` - 转型后的 `obj is class(ClassA)` 输出 `1` - 转型后的 `obj is class(ClassB)` 输出 `0` `Inherited;`: 代码块身份:可直接照写示例 ```tsl obj := new B(); obj.Speak(); type A = class public function Speak(); virtual; begin writeLn(1); end; end; type B = class(A) public function Speak(); override; begin Inherited; writeLn(2); end; end; ``` 输出说明: - `Inherited;` 会先执行父类同名同参数方法 - 上述例子依次输出 `1`、`2` `Inherited MethodName(...)`: 代码块身份:可直接照写示例 ```tsl obj := new B(); obj.Run(); type A = class public function BaseValue(x); begin writeLn(x + 1); end; end; type B = class(A) public function Run(); begin Inherited BaseValue(5); writeLn(9); end; end; ``` 输出说明: - `Inherited BaseValue(5)` 可以显式调用父类指定方法 - 上述例子依次输出 `6`、`9` 析构函数 `destroy`: 代码块身份:可直接照写示例 ```tsl h := new THuman(); writeLn(class(THuman).GetCount()); h := nil; writeLn(class(THuman).GetCount()); type THuman = class public static mCount; function create(); begin mCount := (mCount ?: 0) + 1; end; function destroy(); begin mCount--; writeLn(mCount); end; class function GetCount(); begin return mCount; end; end; ``` 输出说明: - 创建对象后,`class(THuman).GetCount()` 输出 `1` - `h := nil` 时会触发 `destroy()`,中途输出 `0` - 释放后再次读取 `class(THuman).GetCount()` 也输出 `0` `self(0)` / `self(1)`: 代码块身份:可直接照写示例 ```tsl c := new ChildBox(); a := c.MakeOwner(); b := c.MakeBase(); writeLn(a is class(ChildBox)); writeLn(b is class(ChildBox)); type BaseBox = class public function MakeOwner(); begin return self(1); end; function MakeBase(); begin return self(0); end; end; type ChildBox = class(BaseBox) end; ``` 输出说明: - `self(1)` 返回的对象在这个例子里是 `ChildBox`,因此输出 `1` - `self(0)` 返回的对象在这个例子里不是 `ChildBox`,因此输出 `0` ### 跨 unit 类路径 `unit` 中的嵌套类路径创建属于跨 `unit` 边界,这里用 `text` 展示骨架: 代码块身份:配置片段 / 概念骨架 代码块说明:跨 `unit` 类路径骨架;依赖 `unit` 查找路径和目标环境对象模型,不是可直接复制的单文件最小示例。 ```text // Unit1.tsf unit Unit1; interface type Class1 = class public type Class2 = class public value; end; end; implementation end. // main.tsl uses Unit1; obj := createObject("Unit1.Class1.Class2"); obj.value := 42; writeLn(obj.value); ``` 输出说明: - 这个骨架只说明字符串路径创建需要按跨 `unit` 边界处理。 - 生成实际代码前先回看 [09_units_and_scope.md](09_units_and_scope.md) 和 [19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md)。 - 不要把它直接改写成普通本地类的 `new ClassName()` 模式。 继承单元中的嵌套类路径属于跨 `unit` 边界,这里也用 `text` 展示骨架: 代码块身份:配置片段 / 概念骨架 代码块说明:跨 `unit` 父类路径骨架;依赖 `unit` 查找路径和目标环境对象模型,不是可直接复制的单文件最小示例。 ```text // Unit1.tsf unit Unit1; interface type Class1 = class public type Class2 = class public value; end; end; implementation end. // main.tsl uses Unit1; type MyNewClass = class(Unit1.Class1.Class2) end; ``` 输出说明: - 这个骨架只说明继承声明里的父类路径形态属于跨 `unit` 边界。 - 不要把它泛化成普通表达式 `class(Unit1.Class1.Class2)`。 - 对象创建方式不要从普通本地类 `new ClassName()` 规则直接外推。 能力探测: 代码块身份:可直接照写示例 ```tsl {$ifdef parentClassInUnit} writeLn(1); {$else} writeLn(0); {$endif} ``` 输出说明: - `{$ifdef parentClassInUnit}` 输出 `1` ## 默认生成模板 最短类骨架直接复用本页开头的“最短类骨架”。 代码块身份:可直接照写示例 ```tsl type Person = class end; ``` ## 禁止项 - 以下误写不可照写;本节只收容易被智能体从相近语言或相邻 TSL 写法外推出来的边界,不重复列已经在核心规则里明确的普通规则。 - 不把裸类名成员访问当成文档事实;类方法和静态字段默认用 `class(Name).Member` 或文档明确反射入口。 - 把 `self()` 当成本页明确的工厂式写法。 - 把 `private` / `protected create` 当成一定会自动执行的构造函数。 - 在子类里把父类 `private` 成员当成 `protected` 成员来访问。 - 把带命名空间的 `new a.b.c` 直接当成文档事实。 - 把 `class(Unit1.Class1.Class2)` 这种表达式写法直接当成文档事实。 代码块身份:反例 / 不可照写 ```text obj := new Unit1.Class1.Class2(); ``` 上面这种写法不作为可写事实;本页只把 `createObject("Unit1.Class1.Class2")` 这种路径创建写成可用事实。 代码块身份:反例 / 不可照写 ```text cls := class(Unit1.Class1.Class2); ``` 上面这种表达式写法不作为可写事实;本页只把继承声明里的 `class(Unit1.Class1.Class2)` 和字符串路径创建写成可用事实。 代码块身份:反例 / 不可照写 ```text MathBox.Add(1, 2); THuman.mCount := 7; ``` 上面这种裸类名成员访问不作为可写事实;类方法可用 `class(MathBox).Add(...)` 或 `findClass("MathBox").Add(...)` 调用,静态字段可用 `class(THuman).mCount` 访问。 代码块身份:反例 / 不可照写 ```text self(); ``` 上面这种无参工厂式 `self()` 不作为可写事实;本页只把 `self(0)` / `self(1)` 这种工厂式写成可用事实。普通成员访问仍直接写成员名,不加 `self` 前缀。