playbook/docs/tsl/syntax/08_objects_and_classes.md

31 KiB
Raw Blame History

TSL 对象与类

文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;02_core_model.md(优先)、09_units_and_scope.md20_object_runtime_and_introspection.md21_builtin_runtime_objects.md24_object_overloads_and_iteration.md11_pitfalls.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md

这一篇收拢面向对象语法,只保留语言层规则。

本篇职责

回答“type Name = class、字段、static、方法、property、析构、类类型、继承和对象创建在 TSL 里怎样写”。

本页后半段有少量依赖 unit 的双文件示例;如果你还没建立 unit / uses 的多文件心智模型,先看 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.md21_builtin_runtime_objects.md24_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(...)newcreateObject("ClassName", ...)createObject(ClassType, ...) 都可以透传构造参数,也都支持默认参数和命名参数。
  • 析构写法是无参 function destroy();;把对象引用设为 nil 时会触发它。
  • 工厂式 self(0) / self(1) 可用;不要生成 self() 这种无参工厂式写法。
  • unit 类路径创建和继承属于多文件边界;先回看 09_units_and_scope.md19_namespace_libpath_and_unit_runtime.md,不要从普通单文件 new / createObject 规则直接泛化。
  • {$ifdef parentClassInUnit} 为真,可用于探测“继承和构造单元中的类”能力是否可用。
  • private / protected / public 可用;类开头未写可见性时,成员默认按 public 处理,但生成代码默认显式写 public
  • 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。
  • 外部访问 private / protected 字段或方法,会在执行时报对象成员访问错误。
  • 子类可以直接访问父类 protected 成员;不能直接访问父类 private 成员。
  • create 写成 privateprotected 时,对象仍能创建,但 newcreateObject(...) 不会执行该构造逻辑;构造函数应保持 public
  • 更复杂的多重继承边界和更复杂的对象模型不在本页展开。

可直接照写示例

使用这些示例时遵守:

  • 普通本地类创建优先复制 new ClassName() 形态。
  • createObject(...) 示例只在字符串类名、类类型变量或跨 unit 路径场景复制。
  • property 类型注解只有在已有类型名证据时生成;不要为了完整性发明说明性类型名。

最小类与声明位置

最短类骨架:

如果任务只是要“先写出一个类”,先从这个骨架起手;后面的例子再逐步进入字段、工厂函数和声明位置边界。

代码块身份:可直接照写示例

type Person = class
end;

字段、方法和 new

代码块身份:可直接照写示例

type Person = class
public
    name;
    function SetName(new_name);
    begin
        name := new_name;
    end;
end;

function MakePerson();
begin
    return new Person();
end;

类声明位置:

代码块身份:可直接照写示例

obj := new MyClass();
obj.value := 5;
writeLn(obj.value);

type MyClass = class
public
    value;
end;

输出说明:

  • 输出 5
  • 说明顶层类声明可以放在松散语句之后
  • 同时也说明 new MyClass() 可以解析到后面才出现的类声明

代码块身份:输出片段

5

顶层函数后面也可以继续声明类:

代码块身份:可直接照写示例

function MakeObj();
begin
    return 1;
end;

type MyClass = class
public
    value;
end;

结果说明:

  • 上述声明顺序可以编译通过

类声明位置的反例:

代码块身份:反例 / 不可照写

// 函数体内部声明类
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 字段:

代码块身份:可直接照写示例

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 成员:

代码块身份:可直接照写示例

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;

输出说明:

  • 依次输出 1211111
  • 说明类里可以定义实例常量和 static const
  • 说明实例常量可通过实例读取,static const 可通过 class(C) 读取
  • 说明成员常量也可用于成员函数默认参数

成员方法内直接访问成员:

代码块身份:可直接照写示例

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

代码块身份:可直接照写示例

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 处理。
  • 上述例子依次输出 95

protected 可供子类直接访问:

代码块身份:可直接照写示例

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 方法。
  • 上述例子依次输出 6688

private 的失败场景,这里用 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

代码块身份:可直接照写示例

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 输出 <NIL>,说明 private create 没有执行。
  • protected createcreateObject("A", ...)createObject(class(A), ...) 也按同一规则处理;构造函数应保持 public

属性、类型注解与类外实现

基础 property

代码块身份:可直接照写示例

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

代码块身份:可直接照写示例

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

字段和类方法类型注解:

代码块身份:可直接照写示例

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; 可以作为类方法返回值类型注解。
  • 上述例子依次输出 abc7abc

代码块身份:输出片段

abc
7
abc

类内声明、类外实现的带类型重载方法:

代码块身份:可直接照写示例

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<NIL>left2left

代码块身份:输出片段

left
<NIL>
left
2
left

索引型 property

代码块身份:可直接照写示例

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

代码块身份:可直接照写示例

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

固定字符串索引:

代码块身份:可直接照写示例

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

代码块身份:可直接照写示例

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 方法参数:

代码块身份:可直接照写示例

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 关键字:

代码块身份:可直接照写示例

type Person = class
end;

function MakePerson();
begin
    return new Person();
end;

通过类类型创建对象:

代码块身份:可直接照写示例

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) 取得类类型:

代码块身份:可直接照写示例

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

构造参数、默认值和命名参数:

代码块身份:可直接照写示例

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) 依次输出 222099
  • createObject("Person", 11) 依次输出 112030
  • 说明构造函数默认参数和命名参数不只适用于普通函数调用,也适用于对象创建

类类型也同样支持默认参数和命名参数:

代码块身份:可直接照写示例

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) 依次输出 332077
  • 因此类类型入口下的 createObject(cls, ...) 也遵循同样的构造参数规则

class function

代码块身份:可直接照写示例

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(...) 可以调用类方法
  • 上述例子依次输出 711

类方法、重载、继承与析构

overload 方法:

代码块身份:可直接照写示例

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;

输出说明:

  • 依次输出 315
  • 说明同名不同参数个数的方法可以共存

基础继承:

代码块身份:可直接照写示例

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

多重继承:

代码块身份:可直接照写示例

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) 可以同时继承 AB 的方法
  • 上述例子依次输出 12

多重继承下的同名方法优先级:

代码块身份:可直接照写示例

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

代码块身份:可直接照写示例

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()

代码块身份:可直接照写示例

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) 强制转型:

代码块身份:可直接照写示例

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;

代码块身份:可直接照写示例

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; 会先执行父类同名同参数方法
  • 上述例子依次输出 12

Inherited MethodName(...)

代码块身份:可直接照写示例

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) 可以显式调用父类指定方法
  • 上述例子依次输出 69

析构函数 destroy

代码块身份:可直接照写示例

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)

代码块身份:可直接照写示例

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 查找路径和目标环境对象模型,不是可直接复制的单文件最小示例。

// 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 边界,这里也用 text 展示骨架:

代码块身份:配置片段 / 概念骨架 代码块说明:跨 unit 父类路径骨架;依赖 unit 查找路径和目标环境对象模型,不是可直接复制的单文件最小示例。

// 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() 规则直接外推。

能力探测:

代码块身份:可直接照写示例

{$ifdef parentClassInUnit}
writeLn(1);
{$else}
writeLn(0);
{$endif}

输出说明:

  • {$ifdef parentClassInUnit} 输出 1

默认生成模板

最短类骨架直接复用本页开头的“最短类骨架”。

代码块身份:可直接照写示例

type Person = class
end;

禁止项

  • 以下误写不可照写;本节只收容易被智能体从相近语言或相邻 TSL 写法外推出来的边界,不重复列已经在核心规则里明确的普通规则。
  • 不把裸类名成员访问当成文档事实;类方法和静态字段默认用 class(Name).Member 或文档明确反射入口。
  • self() 当成本页明确的工厂式写法。
  • private / protected create 当成一定会自动执行的构造函数。
  • 在子类里把父类 private 成员当成 protected 成员来访问。
  • 把带命名空间的 new a.b.c 直接当成文档事实。
  • class(Unit1.Class1.Class2) 这种表达式写法直接当成文档事实。

代码块身份:反例 / 不可照写

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

上面这种写法不作为可写事实;本页只把 createObject("Unit1.Class1.Class2") 这种路径创建写成可用事实。

代码块身份:反例 / 不可照写

cls := class(Unit1.Class1.Class2);

上面这种表达式写法不作为可写事实;本页只把继承声明里的 class(Unit1.Class1.Class2) 和字符串路径创建写成可用事实。

代码块身份:反例 / 不可照写

MathBox.Add(1, 2);
THuman.mCount := 7;

上面这种裸类名成员访问不作为可写事实;类方法可用 class(MathBox).Add(...)findClass("MathBox").Add(...) 调用,静态字段可用 class(THuman).mCount 访问。

代码块身份:反例 / 不可照写

self();

上面这种无参工厂式 self() 不作为可写事实;本页只把 self(0) / self(1) 这种工厂式写成可用事实。普通成员访问仍直接写成员名,不加 self 前缀。