✨ feat(skills): add tsl syntax reference skill
This commit is contained in:
@@ -0,0 +1,606 @@
|
||||
# TSL 对象运行时与反射
|
||||
|
||||
文档类型:语法深水专题
|
||||
是否可直接用于生成代码:仅部分
|
||||
是否含可直接照写示例:是
|
||||
是否含不可照写反例:是
|
||||
遇到不确定时:先按本页候选页继续判断;[08_objects_and_classes.md](08_objects_and_classes.md)、[16_lexical_structure_and_compile_options.md](16_lexical_structure_and_compile_options.md)、[21_builtin_runtime_objects.md](21_builtin_runtime_objects.md)、[24_object_overloads_and_iteration.md](24_object_overloads_and_iteration.md);仍在语法层但不命中时回到本 Skill 路由中心 [index.md](index.md);如果问题已经超出语法层,停止并交接给对应事实所有者
|
||||
|
||||
这一篇收拢对象模型里不适合继续堆在基础类主线里的运行时内容:类信息、函数句柄、对象状态、引用计数、运行时对象枚举、弱引用与自动弱引用。普通类声明、继承、构造和普通对象创建仍以 [08_objects_and_classes.md](08_objects_and_classes.md) 为主线;本页只在用户明确需要运行时反射、内省、对象生命周期或弱引用能力时进入。
|
||||
|
||||
## 本篇职责
|
||||
|
||||
回答“类已经会声明、继承、构造之后,怎样检查类信息、函数信息、对象运行时状态、弱引用创建和弱引用访问判定”。如果只是写一个类、创建一个对象、调用对象方法,回到 [08_objects_and_classes.md](08_objects_and_classes.md),不要因为本页存在反射或弱引用能力就改写成动态查找。
|
||||
|
||||
## 智能体对象运行时/反射判断流程
|
||||
|
||||
1. 先判断用户是否明确要求运行时类类型、方法句柄、对象状态、对象枚举、反射信息或弱引用/自动弱引用。
|
||||
2. 普通对象创建、普通方法调用、类声明和继承都回到 [08_objects_and_classes.md](08_objects_and_classes.md),不要把 `findClass(...)` / `createObject(...)` 当默认写法。
|
||||
3. 确实命中反射时,入口优先照 `findClass`、`findFunction`、`thisFunction`、`findOverLoad` 等文档明确示例写。
|
||||
4. 访问弱引用前先做 `checkWeakRef(...)` 判定,不要假设失效弱引用安全返回 `nil`。
|
||||
5. 类内段落式 `weakRef` / `autoRef`(不带分号)是合法的成员弱引用开关;只有带分号的 `weakRef;` / `autoRef;` 才报 `invalid class definition`。
|
||||
6. 函数值调用边界回看函数页,避免把函数指针直接当普通函数调用。
|
||||
7. 没有对应代码块时不要发明对象运行时/反射/弱引用写法。
|
||||
|
||||
## 核心规则
|
||||
|
||||
- 对象值可以用 `ifObj(...)` 做显式判定。
|
||||
- 普通本地类创建仍默认使用 `new ClassName()`;本页的 `createObject(cls)` 只用于运行时已经拿到类类型变量后的创建场景。
|
||||
- `class(Name)` 和 `findClass("Name")` 都可以拿到类类型。
|
||||
- `obj is classType` 可直接拿类类型变量来做实例判定。
|
||||
- `findClass("ParentName", obj)` 可以把实例对象降成指定父类视图。
|
||||
- `obj.classInfo(1)` 会返回类类型,并且这个类类型可以继续交给 `createObject(...)`。
|
||||
- `obj.classInfo()` 至少可以直接读出 `["classname"]`。
|
||||
- `objectstate(self)` 在构造函数内部返回 `1`;构造结束后对实例再取 `objectstate(obj)` 返回 `2`。
|
||||
- `tslassigning` 在属性写入函数里输出 `1`,在属性读取函数里输出 `0`。
|
||||
- `findFunction("MethodName", obj)` 可以拿到实例方法句柄。
|
||||
- `findFunction("MethodName", class(A))` 可以拿到类方法句柄。
|
||||
- `thisFunction(obj.Method)` 和 `thisFunction(class(A).Method)` 也可用。
|
||||
- 句柄调用方式默认写 `f.do(...)`。
|
||||
- `findOverLoad(argc, "Name", obj)` 可以按参数个数取到对应重载方法。
|
||||
- `functionInfo` 至少可以读出 `functionname`、`returntype`、`classname` 这几个字段。
|
||||
- `tslObjects(1)` 会按类名分组返回对象信息,并且分组项里的 `"obj"` 字段可以重新拿到可调用对象。
|
||||
- 对象赋值会延长对象存活;只有最后一个引用清空后才会触发 `destroy()`。
|
||||
- `self` 在本页只用于需要本实例对象引用的运行时函数,例如 `objectstate(self)`;普通成员访问仍按 [08_objects_and_classes.md](08_objects_and_classes.md) 的规则,不加 `self` 前缀。
|
||||
- 弱引用能力的条件编译宏是 `weakptr`;自动弱引用相关宏是 `autoWeak`。
|
||||
- `weakRef(obj)` 可以创建弱引用;对象仍存活时,`weakref_get(w)` 可以拿回强引用。
|
||||
- `checkWeakRef(w)` 在对象仍存活时返回 `1`,对象已释放后返回 `-1`。
|
||||
- 对已经失效的弱引用直接调用 `weakref_get(w)` 会运行报错,不要把它当成“安全返回 nil”的接口。
|
||||
- 类成员上的 `[weakRef] field;` 和 `[autoRef] field;` 可以通过。
|
||||
- `[weakRef]` 成员不会阻止对象析构;强引用释放后,对象会正常销毁。
|
||||
- 类内段落式 `weakRef` / `autoRef`(不带分号)像 `public` / `private` 一样切换后续成员的弱引用属性,可写;只有带分号的 `weakRef;` / `autoRef;` 才报 `invalid class definition`。
|
||||
|
||||
## 可直接照写示例
|
||||
|
||||
### 对象值与类类型
|
||||
|
||||
对象值的最小显式判定:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
obj := new RuntimeBox();
|
||||
writeLn(ifObj(obj));
|
||||
|
||||
type RuntimeBox = class
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出 `1`
|
||||
- 说明 `new ...()` 得到的普通对象值可以用 `ifObj(...)` 判定
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
`findClass(...)` 与类类型变量:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
classA := findClass("A");
|
||||
obj := new A();
|
||||
writeLn(classA.FuncA());
|
||||
writeLn(obj is classA);
|
||||
|
||||
type A = class
|
||||
public
|
||||
class function FuncA();
|
||||
begin
|
||||
return "ClassA";
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `ClassA`、`1`
|
||||
- 说明 `findClass("A")` 可以拿到类类型
|
||||
- 说明类类型变量可以直接调用类方法
|
||||
- 也说明 `obj is classA` 这种“实例对类类型变量做判定”的写法可用
|
||||
|
||||
`findClass("Parent", obj)` 的父类视图:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
objb := new ClassB();
|
||||
obj := findClass("ClassA", objb);
|
||||
writeLn(obj.Func());
|
||||
|
||||
type ClassA = class
|
||||
public
|
||||
function Func(); virtual;
|
||||
begin
|
||||
return "ClassA";
|
||||
end;
|
||||
end;
|
||||
type ClassB = class(ClassA)
|
||||
public
|
||||
function Func(); override;
|
||||
begin
|
||||
return "ClassB";
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出 `ClassA`
|
||||
- 说明 `findClass("ClassA", objb)` 可以把子类实例降成父类视图
|
||||
- 也说明之后的方法分派会按父类视图执行
|
||||
|
||||
### 类信息与对象状态
|
||||
|
||||
`classInfo(1)` 返回类类型:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
obj := new A();
|
||||
cls := obj.classInfo(1);
|
||||
obj2 := createObject(cls);
|
||||
writeLn(obj2 is class(A));
|
||||
|
||||
type A = class
|
||||
public
|
||||
value;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出 `1`
|
||||
- 说明 `obj.classInfo(1)` 可以返回类类型
|
||||
- 也说明这个类类型可以继续交给 `createObject(...)`
|
||||
- 这个 `createObject(cls)` 只适用于“类类型来自运行时”的场景;普通本地类创建仍回 [08_objects_and_classes.md](08_objects_and_classes.md) 使用 `new ClassName()`
|
||||
|
||||
`classInfo()` 返回的映射字段:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
obj := new A();
|
||||
info := obj.classInfo();
|
||||
writeLn(info["classname"]);
|
||||
|
||||
type A = class
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出 `a`
|
||||
- 说明 `classInfo()` 至少可以直接读出 `["classname"]`
|
||||
- 这个最小例子里,类名表现为小写标识符
|
||||
|
||||
`objectstate(...)`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
oa := new Ca("abc");
|
||||
writeLn(objectstate(oa));
|
||||
|
||||
type Ca = class
|
||||
public
|
||||
static sca;
|
||||
function create(n);
|
||||
begin
|
||||
sca := self;
|
||||
writeLn(objectstate(self));
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `1`、`2`
|
||||
- 说明构造函数内部对象状态是 `1`
|
||||
- 构造结束后,再对实例取 `objectstate(...)` 会得到 `2`
|
||||
|
||||
`tslassigning`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
o := new Ca();
|
||||
o.c := 3;
|
||||
writeLn(o.c);
|
||||
|
||||
type Ca = class
|
||||
public
|
||||
value;
|
||||
property c read Getc write Setc;
|
||||
function Setc(v);
|
||||
begin
|
||||
writeLn(tslassigning);
|
||||
value := v;
|
||||
end;
|
||||
function Getc();
|
||||
begin
|
||||
writeLn(tslassigning);
|
||||
return value;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `1`、`0`、`3`
|
||||
- 说明属性写入函数里 `tslassigning` 为 `1`
|
||||
- 属性读取函数里 `tslassigning` 为 `0`
|
||||
|
||||
### 函数句柄与重载
|
||||
|
||||
`findFunction(...)` 查找实例方法和类方法:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
obj := new A();
|
||||
f1 := findFunction("Add", obj);
|
||||
f2 := findFunction("AddClass", class(A));
|
||||
writeLn(f1.do(3, 4));
|
||||
writeLn(f2.do(5, 6));
|
||||
|
||||
type A = class
|
||||
public
|
||||
function Add(x, y);
|
||||
begin
|
||||
return x + y;
|
||||
end;
|
||||
class function AddClass(x, y);
|
||||
begin
|
||||
return x + y;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `7`、`11`
|
||||
- 说明 `findFunction(..., obj)` 能拿到实例方法句柄
|
||||
- 说明 `findFunction(..., class(A))` 能拿到类方法句柄
|
||||
- 也说明句柄调用方式默认写 `f.do(...)`
|
||||
|
||||
`thisFunction(...)` 也可以直接拿方法句柄:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
obj := new A();
|
||||
f1 := thisFunction(obj.Add);
|
||||
f2 := thisFunction(class(A).AddClass);
|
||||
writeLn(f1.do(3, 4));
|
||||
writeLn(f2.do(5, 6));
|
||||
|
||||
type A = class
|
||||
public
|
||||
function Add(x, y);
|
||||
begin
|
||||
return x + y;
|
||||
end;
|
||||
class function AddClass(x, y);
|
||||
begin
|
||||
return x + y;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `7`、`11`
|
||||
- 说明 `thisFunction(...)` 也可以稳定得到实例方法和类方法句柄
|
||||
|
||||
`findOverLoad(...)`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
t := findOverLoad(2, "Fun", new TestClass());
|
||||
writeLn(t.do(1, 2));
|
||||
|
||||
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`
|
||||
- 说明 `findOverLoad(2, "Fun", obj)` 可以按参数个数拿到对应重载方法
|
||||
|
||||
### 函数信息、对象枚举与生命周期
|
||||
|
||||
`functionInfo` 的文档字段:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
f := thisFunction(class(A).Add);
|
||||
info := f.functionInfo;
|
||||
writeLn(info["functionname"]);
|
||||
writeLn(info["returntype"]);
|
||||
writeLn(info["classname"]);
|
||||
|
||||
type A = class
|
||||
public
|
||||
class function Add(a: lhs; b: rhs): sum_t;
|
||||
begin
|
||||
return a + b;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `add`、`sum_t`、`a`
|
||||
- 说明 `functionInfo["functionname"]`、`["returntype"]`、`["classname"]` 都可直接读取
|
||||
- 这个最小例子里,返回的函数名和类名表现为小写标识符
|
||||
|
||||
`tslObjects(1)`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
objsInfo := tslObjects(1);
|
||||
writeLn(length(objsInfo["TestClass01"]));
|
||||
newObjA := objsInfo["TestClass01"][0, "obj"];
|
||||
writeLn(newObjA is class(TestClass01));
|
||||
writeLn(newObjA.add(1, 2));
|
||||
|
||||
objA := new TestClass01(100);
|
||||
objB := new TestClass01(101);
|
||||
|
||||
type TestClass01 = class
|
||||
public
|
||||
value;
|
||||
function create(_value);
|
||||
begin
|
||||
value := _value;
|
||||
end;
|
||||
class function Add(x, y);
|
||||
begin
|
||||
return x + y;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `2`、`1`、`3`
|
||||
- 说明 `tslObjects(1)` 可以按类名拿到对象分组
|
||||
- 分组项里的 `"obj"` 字段可以重新拿到可调用对象
|
||||
|
||||
引用计数的最小行为:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
o1 := new T1();
|
||||
o2 := o1;
|
||||
o1 := 0;
|
||||
writeLn("O1 to Zero");
|
||||
o2 := 0;
|
||||
writeLn("O2 to Zero");
|
||||
|
||||
type T1 = class
|
||||
public
|
||||
function destroy();
|
||||
begin
|
||||
writeLn("destroy");
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `O1 to Zero`、`destroy`、`O2 to Zero`
|
||||
- 说明只清空第一个别名时对象还不会释放
|
||||
- 最后一个引用清空后才会触发 `destroy()`
|
||||
|
||||
### 弱引用与自动弱引用
|
||||
|
||||
弱引用能力的条件编译判定:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
{$ifdef weakptr}
|
||||
writeLn('weakptr');
|
||||
{$else}
|
||||
writeLn('Noweakptr');
|
||||
{$endif}
|
||||
{$ifdef autoWeak}
|
||||
writeLn('autoWeak');
|
||||
{$else}
|
||||
writeLn('NoAutoWeak');
|
||||
{$endif}
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 会输出 `weakptr`
|
||||
- 也会输出 `autoWeak`
|
||||
- 说明弱引用能力的条件编译开关应写成 `weakptr`,自动弱引用能力的开关应写成 `autoWeak`
|
||||
|
||||
对象仍然存活时:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
a := new TNode('A');
|
||||
w := weakRef(a);
|
||||
writeLn(checkWeakRef(w));
|
||||
s := weakref_get(w);
|
||||
writeLn(s.name);
|
||||
|
||||
type TNode = class
|
||||
public
|
||||
name;
|
||||
function create(v);
|
||||
begin
|
||||
name := v;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- `checkWeakRef(w)` 返回 `1`
|
||||
- `weakref_get(w)` 能拿回强引用
|
||||
- 通过拿回的强引用可继续访问对象成员,此处输出 `A`
|
||||
|
||||
同一 `TNode` 类型下,强引用释放后的主体模板:
|
||||
|
||||
代码块身份:配置片段 / 概念骨架
|
||||
|
||||
```tsl
|
||||
a := new TNode('A');
|
||||
w := weakRef(a);
|
||||
writeLn(checkWeakRef(w));
|
||||
a := nil;
|
||||
writeLn(checkWeakRef(w));
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 创建后 `checkWeakRef(w)` 返回 `1`
|
||||
- 把最后一个强引用设为 `nil` 后,`checkWeakRef(w)` 返回 `-1`
|
||||
|
||||
失效弱引用不应直接取强引用:
|
||||
|
||||
代码块身份:反例 / 不可照写
|
||||
|
||||
```text
|
||||
a := new TNode('A');
|
||||
w := weakRef(a);
|
||||
a := nil;
|
||||
t := weakref_get(w);
|
||||
```
|
||||
|
||||
上面这种写法不作为可写事实,会运行报错;访问弱引用前应先做 `checkWeakRef(w)` 判定。
|
||||
|
||||
成员级标记可以直接写在类里:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
writeLn(1);
|
||||
|
||||
type TChild = class
|
||||
public
|
||||
[weakRef] owner1;
|
||||
[autoRef] owner2;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 上述写法可以正常执行,并输出 `1`
|
||||
- 说明 `[weakRef] field;` 和 `[autoRef] field;` 都能通过
|
||||
|
||||
`[weakRef]` 成员不会阻止对象析构:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
a := new TA();
|
||||
b := new TB();
|
||||
b.Bind(a);
|
||||
a := nil;
|
||||
writeLn('AfterNil');
|
||||
|
||||
type TA = class
|
||||
public
|
||||
function destroy();
|
||||
begin
|
||||
writeLn('Destroy');
|
||||
end;
|
||||
end;
|
||||
|
||||
type TB = class
|
||||
public
|
||||
[weakRef] owner;
|
||||
function Bind(v);
|
||||
begin
|
||||
owner := v;
|
||||
end;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出顺序是 `Destroy`、`AfterNil`
|
||||
- 说明 `TB.owner` 作为 `[weakRef]` 成员,不会把 `TA` 实例继续强持有
|
||||
|
||||
## 禁止项
|
||||
|
||||
- 不要在运行时/反射页发明类声明、继承或构造语法;类基础回 [08_objects_and_classes.md](08_objects_and_classes.md)。
|
||||
- 不要为了普通对象创建先写 `findClass(...)` / `createObject(...)`;普通本地类创建回 [08_objects_and_classes.md](08_objects_and_classes.md),默认用 `new ClassName()`。
|
||||
- 不要把函数句柄直接当普通函数直调;默认调用方式是 `f.do(...)`。
|
||||
- 不要把 `functionInfo` 示例外推成所有字段都可读。
|
||||
- 不要把引用计数样例泛化成所有对象销毁时机;只按本页文档行为描述。
|
||||
- 不要把 `objectstate(self)` 里的 `self` 泛化成普通成员访问都要加 `self` 前缀。
|
||||
- 不要把弱引用能力的条件编译宏写成 `weakRef`。
|
||||
- 不要以为 `weakref_get(deadWeakRef)` 会像普通可空访问那样安全返回 `nil`。
|
||||
- 不要给段落切换关键字加分号;`weakRef;` / `autoRef;`(带分号)会报 `invalid class definition`,段落式要写成不带分号的 `weakRef` / `autoRef`。
|
||||
- 不要以为 `[weakRef]` 成员会继续强持有对象。
|
||||
|
||||
代码块身份:反例 / 不可照写
|
||||
|
||||
```text
|
||||
type TChild = class
|
||||
public
|
||||
owner0;
|
||||
[weakRef] owner1;
|
||||
weakRef;
|
||||
owner2;
|
||||
autoRef;
|
||||
owner3;
|
||||
end;
|
||||
```
|
||||
|
||||
上面这种给段落切换关键字**加分号**(`weakRef;` / `autoRef;`)的写法会编译失败,错误信息包含 `invalid class definition`。关键在分号:段落切换关键字要像 `public` / `private` 那样**不带分号**独占一行,带分号后被当成独立语句才报错。正确的段落式写法见下一段。
|
||||
|
||||
段落式 `weakRef` / `autoRef`(不带分号)像 `public` / `private` 一样切换后续成员的弱引用属性:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
o := new AutoWeakTest();
|
||||
o.fa := 1;
|
||||
o.fb := 2;
|
||||
writeLn(o.fa, ',', o.fb);
|
||||
|
||||
type AutoWeakTest = class
|
||||
public
|
||||
fa;
|
||||
weakRef
|
||||
fOnClick;
|
||||
fOnDblClick;
|
||||
autoRef
|
||||
fb;
|
||||
end;
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 输出 `1,2`
|
||||
- `weakRef`(不带分号)打开后续成员的自动弱引用开关,`fOnClick` / `fOnDblClick` 成为弱引用成员
|
||||
- `autoRef`(不带分号)关闭开关,`fb` 恢复为强引用成员
|
||||
- 段落切换只影响其后、下一个切换关键字之前的成员;`fa` 在初始(强引用)段
|
||||
- 这与行内 `[weakRef] field;` 是两种可用写法:行内只作用于单个成员,段落式作用于整段
|
||||
Reference in New Issue
Block a user