31 KiB
TSL 对象与类
文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;02_core_model.md(优先)、09_units_and_scope.md、20_object_runtime_and_introspection.md、21_builtin_runtime_objects.md、24_object_overloads_and_iteration.md、11_pitfalls.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md
这一篇收拢面向对象语法,只保留语言层规则。
本篇职责
回答“type Name = class、字段、static、方法、property、析构、类类型、继承和对象创建在 TSL 里怎样写”。
本页后半段有少量依赖 unit 的双文件示例;如果你还没建立 unit / uses 的多文件心智模型,先看 09_units_and_scope.md。
智能体对象/类判断流程
- 先判断要写普通对象、继承、构造函数、类方法、静态字段还是运行时创建。
- 对象声明优先使用本页明确的
type Name = class ... end;骨架。 - 普通本地类创建默认用
new ClassName();只有需要字符串类名、类类型变量或跨unit路径时,才用createObject(...)。 - 类方法和静态字段优先用
class(Name).Member或文档明确反射入口,不要裸写类名调用。 - 构造函数默认保持
public create,不要把private/protected create当成会自动执行的构造函数。 - 普通类成员默认显式写
public段,不依赖隐式 public。 - 方法体内访问当前实例成员默认直接写成员名;成员读写为了性能不加
self前缀。 - 命中对象反射 / 运行时状态、内置运行时对象、对象重载 / 迭代时,分别跳到 20_object_runtime_and_introspection.md、21_builtin_runtime_objects.md、24_object_overloads_and_iteration.md。
- 没有文档事实时不要发明对象/类写法。
核心规则
- 类定义统一按
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)。 - 固定
indexproperty 可用;它可以把某个固定索引直接映射成普通属性读写。 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 和 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类型注解只有在已有类型名证据时生成;不要为了完整性发明说明性类型名。
最小类与声明位置
最短类骨架:
如果任务只是要“先写出一个类”,先从这个骨架起手;后面的例子再逐步进入字段、工厂函数和声明位置边界。
代码块身份:可直接照写示例
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;
输出说明:
- 依次输出
12、11、1、11 - 说明类里可以定义实例常量和
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处理。- 上述例子依次输出
9、5。
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方法。 - 上述例子依次输出
66、88。
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 create、createObject("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输出7b.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;可以作为类方法返回值类型注解。- 上述例子依次输出
abc、7、abc。
代码块身份:输出片段
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>、left、2、left。
代码块身份:输出片段
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"可以写入索引 propertyaa.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"可以写入固定整数索引 propertyaa.idx0输出abcaa.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输出mathaa.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)依次输出22、20、99createObject("Person", 11)依次输出11、20、30- 说明构造函数默认参数和命名参数不只适用于普通函数调用,也适用于对象创建
类类型也同样支持默认参数和命名参数:
代码块身份:可直接照写示例
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:
代码块身份:可直接照写示例
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 方法:
代码块身份:可直接照写示例
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 - 说明同名不同参数个数的方法可以共存
基础继承:
代码块身份:可直接照写示例
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)可以同时继承A和B的方法- 上述例子依次输出
1、2
多重继承下的同名方法优先级:
代码块身份:可直接照写示例
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()输出2class(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;会先执行父类同名同参数方法- 上述例子依次输出
1、2
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)可以显式调用父类指定方法- 上述例子依次输出
6、9
析构函数 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,因此输出1self(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边界处理。 - 生成实际代码前先回看 09_units_and_scope.md 和 19_namespace_libpath_and_unit_runtime.md。
- 不要把它直接改写成普通本地类的
new ClassName()模式。
继承单元中的嵌套类路径属于跨 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 前缀。