29 KiB
Objects And Classes
文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:10_units_and_scope.md(优先)、23_object_runtime_and_introspection.md、12_pitfalls.md
手册位置:第 9 篇,共 32 篇。上一篇:08_control_flow.md。下一篇:10_units_and_scope.md。
这一篇收拢面向对象语法,只保留语言层规则。
这一篇解决什么问题
回答“type Name = class、字段、static、方法、property、析构、类类型、继承和对象创建在 TSL 里怎样写”。
本页后半段有少量依赖 unit 的双文件示例;如果你还没建立 unit / uses 的多文件心智模型,先看 10_units_and_scope.md。
必须记住的规则
- 类定义统一按
type Name = class ... end;写。 - 顶层类声明当前已验证可以放在松散语句之后(单向允许)。
- 顶层类声明当前也已验证可以放在顶层
function / procedure之后。 - 类声明当前没有通过写在函数体内部。
- 在松散语句脚本里,可以先写松散语句再进入顶层类声明;但一旦进入顶层
type Name = class ... end;声明,后面不能再回到松散语句模式。 - 类里可以直接写字段和方法。
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()。 - 已验证的对象创建写法至少有三种:
CreateObject("ClassName")、CreateObject(ClassType)和new ClassName()。 - 如果没有特殊需求,默认优先用
new ClassName();只有需要字符串路径、类类型变量或跨unit路径时,再改用CreateObject(...)。 - 如果类里定义了
function create(...),new、CreateObject("ClassName", ...)和CreateObject(ClassType, ...)都已验证可以透传构造参数,也都已验证支持默认参数和命名参数。 - 当前已验证的析构写法是无参
function destroy();;把对象引用设为nil时会触发它。 - 工厂式
self(0)/self(1)已验证可用;当前解释器没有通过self()这种无参工厂式写法。 - 已做双文件运行验证:如果类定义在
unit的嵌套路径里,可以用CreateObject("Unit1.Class1.Class2")这种字符串路径创建对象。 - 已做双文件运行验证:可以写
type MyNewClass = class(Unit1.Class1.Class2)直接继承单元里的嵌套类路径。 - 当前环境里
{$IFDEF ParentClassInUnit}为真,可用于探测“继承和构造单元中的类”能力是否可用。 private/protected/public已验证可用;类开头未写可见性时,成员默认按public处理。- 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。
- 外部访问
private/protected字段或方法,会在执行时报对象成员访问错误。 - 子类可以通过
self访问父类protected成员;不能通过self访问父类private成员。 create写成private或protected时,对象仍能创建,但new和CreateObject(...)不会执行该构造逻辑;构造函数应保持public。- 更复杂的多重继承边界和更复杂的对象模型还没有在这篇里完整展开。
已验证语法
最短类骨架:
如果你现在只是要“先写出一个类”,先从这个骨架起手;后面的例子再逐步进入字段、工厂函数和声明位置边界。
代码块身份:已验证可执行示例
type Person = class
end;
字段、方法和 CreateObject:
代码块身份:已验证可执行示例
type Person = class
name;
function SetName(new_name);
begin
name := new_name;
end;
end;
function MakePerson();
begin
return CreateObject("Person");
end;
类声明位置:
代码块身份:已验证可执行示例
obj := CreateObject("MyClass");
obj.value := 5;
WriteLn(obj.value);
type MyClass = class
value;
end;
已验证运行结果:
- 输出
5 - 说明在当前解释器里,顶层类声明可以放在松散语句之后
- 同时也说明
CreateObject("MyClass")可以解析到后面才出现的类声明
顶层函数后面也可以继续声明类:
代码块身份:已验证可执行示例
function MakeObj();
begin
return 1;
end;
type MyClass = class
value;
end;
已验证结果:
- 上述
.tsf例子可以编译通过
类声明位置的反例:
代码块身份:反例 / 不可照写
// 函数体内部声明类
function Demo();
begin
type InnerClass = class
value;
end;
return 1;
end;
// 松散语句在 type 之后继续出现
a := 1;
type MyClass = class
value;
end;
WriteLn(a);
已验证结果:
- 函数体内部声明类会报
invalid statement - 在松散语句脚本里,
type MyClass = class ... end;之后继续写WriteLn(a);也会报invalid statement
static 字段:
代码块身份:已验证可执行示例
program test;
type THuman = class
static
mCount;
function create();
begin
mCount := (mCount ?: 0) + 1;
end;
end;
begin
class(THuman).mCount := 100;
WriteLn(class(THuman).mCount);
h := new THuman();
WriteLn(class(THuman).mCount);
WriteLn(h.mCount);
end.
已验证运行结果:
class(THuman).mCount := 100可以直接写静态字段class(THuman).mCount先输出100- 创建对象后,
class(THuman).mCount输出101 - 通过实例读取
h.mCount也输出101
同一字段也接受内联声明:
代码块身份:已验证可执行示例
program test;
type THuman = class
static mCount;
function create();
begin
mCount := (mCount ?: 0) + 1;
end;
end;
begin
class(THuman).mCount := 100;
h := new THuman();
WriteLn(class(THuman).mCount);
end.
已验证运行结果:
static mCount;这种内联写法也可以通过- 上述例子中的
class(THuman).mCount输出101
const 成员:
代码块身份:已验证可执行示例
program test;
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;
begin
o := new C();
WriteLn(o.TestConst());
WriteLn(o.TestConstInParam());
WriteLn(o.mA);
WriteLn(class(C).mB);
end.
已验证运行结果:
- 依次输出
12、11、1、11 - 说明类里可以定义实例常量和
static const - 说明实例常量可通过实例读取,
static const可通过class(C)读取 - 说明成员常量当前也可用于成员函数默认参数
self 指向当前实例:
代码块身份:已验证可执行示例
program test;
type Counter = class
value;
function create(v);
begin
self.value := v;
end;
function Inc();
begin
self.value := self.value + 1;
return self.value;
end;
end;
begin
c := new Counter(10);
WriteLn(c.Inc());
end.
已验证运行结果:
c.Inc()输出11
可见性 private / protected / public:
代码块身份:已验证可执行示例
program test;
type A = class
value;
private
secret;
backup;
public
function create();
begin
self.secret := 2;
self.backup := 3;
end;
function ReadSecret();
begin
return self.secret + self.backup;
end;
end;
begin
a := new A();
a.value := 9;
WriteLn(a.value);
WriteLn(a.ReadSecret());
end.
已验证运行结果:
- 类开头没有显式可见性时,
value默认按public处理,因此a.value := 9可以直接写。 backup跟在private段后面,没有重新切换关键字,因此仍按private处理。- 上述例子依次输出
9、5。
protected 可供子类通过 self 访问:
代码块身份:已验证可执行示例
program test;
type A = class
protected
value;
function Hidden();
begin
return 88;
end;
end;
type B = class(A)
public
function SetValue(v);
begin
self.value := v;
end;
function GetValue();
begin
return self.value;
end;
function TryCall();
begin
return self.Hidden();
end;
end;
begin
b := new B();
b.SetValue(66);
WriteLn(b.GetValue());
WriteLn(b.TryCall());
end.
已验证运行结果:
- 子类里可以通过
self.value访问父类protected字段。 - 子类里也可以通过
self.Hidden()调用父类protected方法。 - 上述例子依次输出
66、88。
private 的失败场景,因为是已验证反例,这里用 text 展示:
代码块身份:反例 / 不可照写
// 外部访问 private 字段
program test;
type A = class
private
secret;
public
function create();
begin
self.secret := 7;
end;
end;
begin
a := new A();
WriteLn(a.secret);
end.
// 子类访问父类 private 字段
program test;
type A = class
private
secret;
public
function ReadSecret();
begin
return self.secret;
end;
end;
type B = class(A)
public
function SetSecret(v);
begin
self.secret := v;
end;
end;
begin
b := new B();
b.SetSecret(77);
WriteLn(b.ReadSecret());
end.
已验证运行结果:
- 外部访问
a.secret会在执行时报对象成员访问错误。 - 子类里通过
self.secret访问父类private字段也会在执行时报错。 - 同样的规则也已单独用
private/protected方法访问做过复核:外部不能调private/protected方法,子类只能调protected方法,不能调private方法。
create 建议保持 public:
代码块身份:已验证可执行示例
program test;
type A = class
public
value;
private
function create(v);
begin
self.value := v;
end;
end;
begin
a := new A(111);
WriteLn(a.value);
end.
已验证运行结果:
a := new A(111)可以创建对象。- 上述例子里的
a.value输出<NIL>,说明private create没有执行。 - 同样的结果已单独用
protected create、CreateObject("A", ...)和CreateObject(class(A), ...)复核;当前解释器里构造函数应保持public。
基础 property:
代码块身份:已验证可执行示例
program test;
type MyBox = class
_value;
function SetValue(v);
begin
if v > 0 then
_value := v;
end;
property Value read _value write SetValue;
end;
begin
b := new MyBox();
b.Value := 7;
WriteLn(b.Value);
b.Value := -1;
WriteLn(b.Value);
end.
已验证运行结果:
b.Value := 7后,b.Value输出7b.Value := -1后,b.Value仍输出7
带类型注解的 property:
代码块身份:已验证可执行示例
program test;
type MyBox = class
_value;
function SetValue(v);
begin
_value := v;
end;
property Value: score_value read _value write SetValue;
end;
begin
b := new MyBox();
b.Value := 9;
WriteLn(b.Value);
end.
已验证运行结果:
property Value: score_value ...这种类型注解写法可以通过- 上述例子中的
b.Value输出9
索引型 property:
代码块身份:已验证可执行示例
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 := new A();
aa.idx(0) := "abc";
WriteLn(aa.idx(0));
end.
已验证运行结果:
aa.idx(0) := "abc"可以写入索引 propertyaa.idx(0)输出abc
固定 index property:
代码块身份:已验证可执行示例
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;
property idx0 index 0 read rIndex write wIndex;
end;
begin
aa := new A();
aa.idx0 := "abc";
WriteLn(aa.idx0);
WriteLn(aa.idx(0));
end.
已验证运行结果:
aa.idx0 := "abc"可以写入固定整数索引 propertyaa.idx0输出abcaa.idx(0)也输出abc
固定字符串索引:
代码块身份:已验证可执行示例
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;
property school index "High school" read rIndex write wIndex;
end;
begin
aa := new A();
aa.school := "math";
WriteLn(aa.school);
WriteLn(aa.idx("High school"));
end.
已验证运行结果:
property school index "High school"这种固定字符串索引写法可以通过aa.school输出mathaa.idx("High school")也输出math
参数化 property:
代码块身份:已验证可执行示例
program test;
type MyDate = class
_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;
begin
d := new MyDate();
d.DateV(2025, 8) := 10;
WriteLn(d.DateV());
end.
已验证运行结果:
d.DateV(2025, 8) := 10可以给参数化 property 赋值d.DateV()输出20250810
参数化 property 的 accessor 方法参数:
代码块身份:已验证可执行示例
program test;
type A = class
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): slot_type read getItem write setItem;
end;
begin
aa := new A();
aa.Item(2) := "x";
WriteLn(aa.Item(2));
end.
已验证运行结果:
read getItem这种“读方法接同参数个数”的写法可以通过write setItem这种“写方法接参数个数 + 赋值值”的写法可以通过- 上述例子中的
aa.Item(2)输出x
new 关键字:
代码块身份:已验证可执行示例
type Person = class
end;
function MakePerson();
begin
return new Person();
end;
通过类类型创建对象:
代码块身份:已验证可执行示例
program test;
type Person = class
value;
end;
begin
cls := FindClass("Person");
obj := CreateObject(cls);
obj.value := 42;
WriteLn(obj.value);
end.
已验证运行结果:
CreateObject(cls)可以通过类类型创建对象- 上述例子中的
obj.value输出42
也可以用 class(Name) 取得类类型:
代码块身份:已验证可执行示例
program test;
type Person = class
value;
function create(v);
begin
value := v;
end;
end;
begin
cls := class(Person);
obj := CreateObject(cls, 44);
WriteLn(obj.value);
end.
已验证运行结果:
class(Person)可以拿到Person的类类型CreateObject(cls, 44)输出44
构造参数、默认值和命名参数:
代码块身份:已验证可执行示例
program test;
type Person = class
a;
b;
c;
function create(a, b = 20, c = 30);
begin
self.a := a;
self.b := b;
self.c := c;
end;
end;
begin
a := CreateObject("Person", 11);
WriteLn(a.a);
WriteLn(a.b);
WriteLn(a.c);
b := new Person(22, c:99);
WriteLn(b.a);
WriteLn(b.b);
WriteLn(b.c);
end.
已验证运行结果:
CreateObject("Person", 11)依次输出11、20、30new Person(22, c:99)依次输出22、20、99- 说明构造函数默认参数和命名参数不只适用于普通函数调用,也适用于对象创建
类类型也同样支持默认参数和命名参数:
代码块身份:已验证可执行示例
program test;
type Person = class
a;
b;
c;
function create(a, b = 20, c = 30);
begin
self.a := a;
self.b := b;
self.c := c;
end;
end;
begin
cls := FindClass("Person");
obj := CreateObject(cls, 33, c:77);
WriteLn(obj.a);
WriteLn(obj.b);
WriteLn(obj.c);
end.
已验证运行结果:
CreateObject(cls, 33, c:77)依次输出33、20、77- 因此类类型入口下的
CreateObject(cls, ...)也遵循同样的构造参数规则
class function:
代码块身份:已验证可执行示例
program test;
type MathBox = class
class function Add(x, y);
begin
return x + y;
end;
end;
begin
cls1 := class(MathBox);
WriteLn(cls1.Add(3, 4));
cls2 := FindClass("MathBox");
WriteLn(cls2.Add(5, 6));
end.
已验证运行结果:
class(MathBox).Add(...)可以调用类方法FindClass("MathBox").Add(...)可以调用类方法- 上述例子依次输出
7、11
overload 方法:
代码块身份:已验证可执行示例
program test;
type TestClass = class
function fun(p1, p2); overload;
begin
return p1 + p2;
end;
function fun(p1); overload;
begin
return p1 + 10;
end;
end;
begin
t := new TestClass();
WriteLn(t.fun(1, 2));
WriteLn(t.fun(5));
end.
已验证运行结果:
- 依次输出
3、15 - 说明同名不同参数个数的方法当前可以共存
基础继承:
代码块身份:已验证可执行示例
program test;
type Animal = class
function Speak();
begin
return 1;
end;
end;
type Dog = class(Animal)
end;
begin
d := new Dog();
WriteLn(d.Speak());
end.
已验证运行结果:
type Dog = class(Animal)可以继承父类实例方法- 上述例子中的
d.Speak()输出1
多重继承:
代码块身份:已验证可执行示例
program test;
type A = class
function Fa();
begin
return 1;
end;
end;
type B = class
function Fb();
begin
return 2;
end;
end;
type C = class(A, B)
end;
begin
obj := new C();
WriteLn(obj.Fa());
WriteLn(obj.Fb());
end.
已验证运行结果:
type C = class(A, B)可以同时继承A和B的方法- 上述例子依次输出
1、2
多重继承下的同名方法优先级:
代码块身份:已验证可执行示例
program test;
type A = class
function Speak();
begin
return 1;
end;
end;
type B = class
function Speak();
begin
return 2;
end;
end;
type C = class(A, B)
end;
begin
obj := new C();
WriteLn(obj.Speak());
end.
已验证运行结果:
- 当多个父类存在同名方法时,当前例子优先命中第一个父类
A - 上述例子中的
obj.Speak()输出1
基础 virtual / override:
代码块身份:已验证可执行示例
program test;
type Animal = class
function Speak(); virtual;
begin
return 1;
end;
end;
type Dog = class(Animal)
function Speak(); override;
begin
return 2;
end;
end;
begin
d := new Dog();
WriteLn(d.Speak());
end.
已验证运行结果:
- 父类
virtual+ 子类override组合可以通过 - 上述例子中的
d.Speak()输出2
class(BaseClass, ObjectName).MethodName():
代码块身份:已验证可执行示例
program test;
type A = class
function Speak(); virtual;
begin
return 1;
end;
end;
type B = class(A)
function Speak(); override;
begin
return 2;
end;
end;
begin
obj := new B();
WriteLn(obj.Speak());
WriteLn(class(A, obj).Speak());
end.
已验证运行结果:
obj.Speak()输出2class(A, obj).Speak()会定向调用祖先类方法,因此输出1
FindClass("ClassName", obj) 强制转型:
代码块身份:已验证可执行示例
program test;
type ClassA = class
function Fuc(); virtual;
begin
return 1;
end;
end;
type ClassB = class(ClassA)
function Fuc(); override;
begin
return 2;
end;
end;
begin
objb := new ClassB();
obj := FindClass("ClassA", objb);
WriteLn(obj.Fuc());
WriteLn(obj is class(ClassA));
WriteLn(obj is class(ClassB));
end.
已验证运行结果:
FindClass("ClassA", objb)会把objb转成ClassA视图- 上述例子中的
obj.Fuc()输出1 - 转型后的
obj is class(ClassA)输出1 - 转型后的
obj is class(ClassB)输出0
Inherited;:
代码块身份:已验证可执行示例
program test;
type A = class
function Speak(); virtual;
begin
WriteLn(1);
end;
end;
type B = class(A)
function Speak(); override;
begin
Inherited;
WriteLn(2);
end;
end;
begin
obj := new B();
obj.Speak();
end.
已验证运行结果:
Inherited;会先执行父类同名同参数方法- 上述例子依次输出
1、2
Inherited MethodName(...):
代码块身份:已验证可执行示例
program test;
type A = class
function BaseValue(x);
begin
WriteLn(x + 1);
end;
end;
type B = class(A)
function Run();
begin
Inherited BaseValue(5);
WriteLn(9);
end;
end;
begin
obj := new B();
obj.Run();
end.
已验证运行结果:
Inherited BaseValue(5)可以显式调用父类指定方法- 上述例子依次输出
6、9
析构函数 destroy:
代码块身份:已验证可执行示例
program test;
type THuman = class
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;
begin
h := new THuman();
WriteLn(class(THuman).GetCount());
h := nil;
WriteLn(class(THuman).GetCount());
end.
已验证运行结果:
- 创建对象后,
class(THuman).GetCount()输出1 h := nil时会触发destroy(),中途输出0- 释放后再次读取
class(THuman).GetCount()也输出0
self(0) / self(1):
代码块身份:已验证可执行示例
program test;
type BaseBox = class
function MakeOwner();
begin
return self(1);
end;
function MakeBase();
begin
return self(0);
end;
end;
type ChildBox = class(BaseBox)
end;
begin
c := new ChildBox();
a := c.MakeOwner();
b := c.MakeBase();
WriteLn(a is class(ChildBox));
WriteLn(b is class(ChildBox));
end.
已验证运行结果:
self(1)返回的对象在这个例子里是ChildBox,因此输出1self(0)返回的对象在这个例子里不是ChildBox,因此输出0
unit 中的嵌套类路径创建,因为依赖多文件环境,这里用 text 展示:
代码块身份:配置片段 / 概念骨架 代码块说明:已在多文件环境下验证;这里只用来展示结构和结果,不是可直接复制的单文件最小示例。
// Unit1.tsf
unit Unit1;
interface
type Class1 = class
type Class2 = class
value;
end;
end;
implementation
end.
// main.tsl
program test;
begin
obj := CreateObject("Unit1.Class1.Class2");
obj.value := 42;
WriteLn(obj.value);
end.
已验证运行结果:
CreateObject("Unit1.Class1.Class2")可以创建对象- 上述例子中的
obj.value输出42
继承单元中的嵌套类路径,因为依赖多文件环境,这里也用 text 展示:
代码块身份:配置片段 / 概念骨架 代码块说明:已在多文件环境下验证;这里只用来展示结构和结果,不是可直接复制的单文件最小示例。
// Unit1.tsf
unit Unit1;
interface
type Class1 = class
type Class2 = class
value;
end;
end;
implementation
end.
// main.tsl
program test;
type MyNewClass = class(Unit1.Class1.Class2)
end;
begin
obj := CreateObject("MyNewClass");
obj.value := 42;
WriteLn(obj.value);
end.
已验证运行结果:
type MyNewClass = class(Unit1.Class1.Class2)可以通过并继承该路径类- 上述例子中的
obj.value输出42
能力探测:
代码块身份:已验证可执行示例
program test;
begin
{$IFDEF ParentClassInUnit}
WriteLn(1);
{$ELSE}
WriteLn(0);
{$ENDIF}
end.
已验证运行结果:
- 当前环境里
{$IFDEF ParentClassInUnit}输出1
最小可编译示例
如果你只是想先起一个最短类骨架,直接复用本页开头的“最短类骨架”。
代码块身份:已验证可执行示例
type Person = class
end;
常见误写
- 以下误写中,有代码块的条目都已经单独验证;没有单独代码块展开的文字条目,属于已知语义陷阱或错误泛化,不要把它们当成可写事实。
- 把类写成裸
class Person。 - 还没建立类定义就直接写
CreateObject("Person")或new Person()。 - 把
MathBox.Add(...)这种裸类名调用直接当成当前解释器已验证的类方法调用写法。 - 把
THuman.mCount这种裸类名静态字段访问直接当成当前解释器已验证事实。 - 把
self()当成当前解释器已验证的工厂式写法。 - 把
private/protected create当成当前解释器一定会自动执行的构造函数。 - 在子类里把父类
private成员当成protected成员来访问。 - 把带命名空间的
new a.b.c直接当成当前解释器已支持事实。 - 把
class(Unit1.Class1.Class2)这种表达式写法直接当成当前解释器已验证事实。 - 以为类声明既然能放在松散语句后面,就还能在
type后面继续回到松散语句模式。
代码块身份:反例 / 不可照写
obj := new Unit1.Class1.Class2();
上面这种写法在当前已记录验证里没有通过;当前只把 CreateObject("Unit1.Class1.Class2") 这种路径创建写成已验证事实。
代码块身份:反例 / 不可照写
cls := class(Unit1.Class1.Class2);
上面这种表达式写法在当前解释器里没有通过;当前只把继承声明里的 class(Unit1.Class1.Class2) 和字符串路径创建写成已验证事实。
代码块身份:反例 / 不可照写
MathBox.Add(1, 2);
上面这种裸类名调用在当前解释器里没有通过;当前已验证可用的是先拿到类类型,再用 class(MathBox).Add(...) 或 FindClass("MathBox").Add(...) 调用。
代码块身份:反例 / 不可照写
THuman.mCount := 7;
上面这种裸类名静态字段访问在当前解释器里没有通过;当前已验证可用的是 class(THuman).mCount。
代码块身份:反例 / 不可照写
self();
上面这种无参工厂式 self() 在当前解释器里没有通过;当前只把 self 实例引用,以及 self(0) / self(1) 写成已验证事实。
代码块身份:反例 / 不可照写
a := 1;
type MyClass = class
value;
end;
WriteLn(a);
上面这种写法在当前解释器里没有通过,会报 invalid statement。当前只把“松散语句后面可以接顶层类声明”写成已验证事实,不把它泛化成“type 之后还能继续写松散语句”。
跳转指引
- 回看 unit 与作用域:见 10_units_and_scope.md
- 最后统一扫坑:见 12_pitfalls.md
- 继续看对象运行时和更深对象能力:见 23_object_runtime_and_introspection.md 和 32_object_overloads_and_iteration.md