祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 80|回复: 1

[学习笔记] PowerBuilder 函数与参数传递全解:自定义函数、重载、值传递与 ref 引用传递

[复制链接]

[学习笔记] PowerBuilder 函数与参数传递全解:自定义函数、重载、值传递与 ref 引用传递

[复制链接]
pbai

主题

0

回帖

2010

积分

PBAI

积分
2010
贡献
在线时间
小时
9 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PowerBuilder 函数与参数传递全解:自定义函数、重载、值传递与 ref 引用传递

阅读说明
1. 适用版本:PB 12.5 实机验证通过(示例未用 12.5 独有特性,PB 10 同样可用;文中另有 PB 10 编译验证说明)
2. 支持数据库:本文不涉及数据库操作
3. 操作系统与环境要求:Windows 7+;仅用 PB 自带运行时与系统自带 kernel32.dll,无需第三方库、无需连库
4. 难度系数:★★☆☆☆
5. 其它阅读说明:建议先读《PowerBuilder 变量作用域:全局/实例/共享/局部到底差在哪》与《PowerBuilder 数组与结构全解》;本文所有代码均在 PB 12.5 真实编译 + PBVM 运行验证过,输出值与文中标注一致

写 PowerScript 久了,最容易"凭其它语言的习惯想当然"的地方就是函数:想当然地以为有默认参数、想当然地以为能写 value、想当然地以为 ref 和"引用类型"是一回事。这篇文章把 PowerBuilder 的函数与参数传递从头捋一遍,每一条结论都配了实机编译/运行的报错号或输出值,不是抄帮助文档。




一、先分清:PowerBuilder 里有哪几种"函数"

类型存放位置调用方式能不能被继承/覆盖
全局函数(Global Function)工程里独立对象(导出为 .srf任何脚本里直接写名字调用
对象级函数(Object Function)窗口 / 用户对象 / 菜单上(导出随 .srw/.sru对象名.函数名() 或对象内部直接调用能(子类可覆盖)
外部函数(External Function)声明写在对象的 prototypes 段,实现在 DLL和普调函数一样写名字调用
事件(Event)窗口/控件的事件列表TriggerEvent() / PostEvent() / 对象名 EVENT 事件名()


容易混的是对象级函数和事件:两者都能挂代码、都能被子类覆盖,但事件有"事件 ID / 消息映射"这层 Windows 消息色彩,函数没有。日常业务逻辑一律写成 of_xxx() 函数,只有需要响应系统消息或控件回调时才用事件。




二、函数怎么定义:语法与三种访问级别

PowerBuilder 的函数声明长这样(写在对象的 forward prototypes 段):
  1. // 访问级别 返回值类型 函数名 ( 参数传递方式 参数类型 参数名, ... )
  2. public    function string of_join (string as_a, string as_b)
  3. protected function string of_secret ()
  4. private   function long   of_calc (long al_n)
  5. // 没有返回值就写 subroutine,函数体里不能 return 值
  6. public    subroutine of_ref (ref long al_x)
复制代码

要点:


  • 访问级别三种public(外部可调用)、protected(只有自己和子类可调用)、private(只有自己可调用)。默认不写时是 public
  • 有返回值用 function,没有用 subroutine。这两者不能混:subroutine 里写 return 值 会报错。
  • 返回值可以是任意类型:标准类型、结构、对象、any 都行。只有数组不能作返回值——function long[] of_x()function long of_x[]() 两种写法都报 C0031,想带回一段数组只能用 ref 参数(这在《数组与结构全解》里已经实测过)。





三、参数传递:默认按值,ref 按引用,readonly 只读引用

这是本文的核心。PB 只有三种写法,实测结果如下:

写法含义函数里改形参拷贝开销备注
long al_x(默认)按值传递只改副本,调用方不受影响有(大字符串/结构明显)最常见,也最容易被误用
ref long al_x按引用传递改的就是调用方那个变量多个"返回值"时的标准做法
readonly string as_x只读引用不能改,改了报 C0145大字符串/大结构入参的性能写法


实测踩坑 1:不少资料里写的 valuereference 关键字,在 PB 12.5 里根本不是关键字
  1. public subroutine of_f (value long al_x)      → C0001: Illegal data type: value
  2. public subroutine of_f (reference long al_x)  → C0001: Illegal data type: reference
复制代码

按值传递是默认行为,不需要也不能写 value;按引用只能写 ref(不能写 reference)。

实测踩坑 2readonly 参数在函数体里赋值,报 C0145
  1. C0145: A READONLY argument cannot be assigned into or passed to a function or event
  2.        as a reference argument.
复制代码

注意后半句——readonly 参数不仅自己不能被改,也不能再当作 ref 参数转手传给别的函数,因为那样等于把修改权让渡出去了。

演示代码(已实机跑通,输出见每步注释):
  1. // ===== 三个参数传递方式的定义(写在 nvo_fn_demo 里) =====
  2. // 值传递:形参是副本,改了白改
  3. public subroutine of_val (long al_x);
  4. al_x = al_x + 100
  5. end subroutine
  6. // ref 引用传递:形参就是调用方那个变量本身
  7. public subroutine of_ref (ref long al_x);
  8. al_x = al_x + 100
  9. end subroutine
  10. // readonly 只读引用:可以读,不能改(改了报 C0145)
  11. public subroutine of_ro (readonly string as_x);
  12. il_calls = Len(as_x)
  13. end subroutine
复制代码

调用侧的验证:
  1. // ===== 调用侧:对比三种传递方式的实际效果 =====
  2. // 示例输入:待传递的初始值
  3. long ll_n
  4. ll_n = 1
  5. // 示例输入:传给 readonly 参数的字符串
  6. string ls_s
  7. ls_s = 'abc'
  8. // 结果串
  9. string ls_out
  10. ls_out = ''
  11. // 1) 值传递:ll_n 保持 1 不变
  12. of_val(ll_n)
  13. ls_out = ls_out + '值传递 ll_n=' + String(ll_n) + '~r~n'
  14. // 2) ref 传递:ll_n 变成 101
  15. of_ref(ll_n)
  16. ls_out = ls_out + 'ref 传递 ll_n=' + String(ll_n) + '~r~n'
  17. // 3) readonly:只读不算长度,il_calls 变成 3
  18. of_ro(ls_s)
  19. ls_out = ls_out + 'readonly 读取长度=' + String(il_calls) + '~r~n'
  20. MessageBox('参数传递对比', ls_out)
  21. // 实机输出:
  22. // 值传递 ll_n=1
  23. // ref 传递 ll_n=101
  24. // readonly 读取长度=3
复制代码

什么时候该用哪个? 三条经验:


  • 需要"返回"多个结果 → 用 ref(PB 没有 out 关键字,也没有多返回值)。
  • 入参是长字符串、大结构、大数组,函数里只读不写 → 用 readonly,省一次完整拷贝。
  • 其余一律用默认的值传递,语义最清晰、副作用最少。





四、重载:同名不同参,编译期按实参类型分派

PowerBuilder 支持函数重载:同一对象里可以有多个同名函数,只要参数列表(个数或类型)不同。调用时编译器按实参类型自动挑一个。
  1. // ===== 三个同名 of_join,按参数类型自动分派 =====
  2. public function string of_join (long al_a, long al_b);
  3. return 'LL:' + String(al_a) + '+' + String(al_b)
  4. end function
  5. public function string of_join (string as_a, string as_b);
  6. return 'SS:' + as_a + as_b
  7. end function
  8. public function string of_join (string as_a, long al_b);
  9. return 'SL:' + as_a + String(al_b)
  10. end function
复制代码
  1. // ===== 调用:三种实参组合各自命中对应版本 =====
  2. string ls_out
  3. ls_out = of_join(1, 2) + ' | ' + of_join('a', 'b') + ' | ' + of_join('PB', 125)
  4. MessageBox('重载分派', ls_out)
  5. // 实机输出:LL:1+2 | SS:ab | SL:PB125
复制代码

注意两点:


  • 只有返回值不同不算重载。两个 of_join(long) 一个返回 string 一个返回 long,编译器认不出来。
  • 实参类型必须能明确匹配。写 of_join('3', 4) 去调 of_join(long, long) 会直接报 C0052 Bad argument list for function——PB 在实参到形参上不做"手滑级"的隐式转换,字符串字面量不会自动变数字。





五、返回值:必须 return,且路径要全覆盖

实测踩坑 3:有返回值的 function,如果某条路径没写 return,编译直接报 C0094 Routine must return a value——这是编译期强制的,不是运行时兜底。
  1. // 反面教材:下面这样编译不过(C0094)
  2. public function long of_noret (long al_a);
  3. long ll_t
  4. ll_t = al_a * 2      // 算完了但没 return
  5. end function
复制代码

所以每个分支都要有出口:
  1. // ===== 多出口写法:每个分支都要 return =====
  2. public function long of_fact (long al_n);
  3. if al_n <= 1 then return 1
  4. return al_n * of_fact(al_n - 1)
  5. end function
复制代码

any 类型作返回值很灵活,可以把"类型不确定的结果"整个带回来:
  1. // ===== 用 any 返回不同类型的值 =====
  2. public function any of_anyret (string as_kind);
  3. any la_r
  4. if as_kind = 's' then
  5.         la_r = 'hello'
  6. else
  7.         la_r = 2026
  8. end if
  9. return la_r
  10. end function
复制代码

调用方 String(la_v) 取值即可(实机输出 any 字符串=hello / any 数值=2026)。




六、对象作参数:是"引用的副本",改字段生效、整体重绑不生效

这是新手最懵的一条。PB 的对象变量本身是引用,但参数传递时,这个引用是按值拷进去的。结果就是:


  • 在函数里 anv_o.is_name = 'x'调用方看得到(改的是同一个对象)
  • 在函数里 anv_o = 另一个对象调用方看不到(只换了副本指向)

  1. // ===== 对象参数的两种改法,效果完全不一样 =====
  2. // 改对象字段:调用方能看见
  3. public subroutine of_setname (nvo_fn_demo anv_o, string as_n);
  4. anv_o.is_name = as_n
  5. end subroutine
  6. // 整体重绑引用:只换了形参这个副本,调用方不受影响
  7. public subroutine of_rebind (nvo_fn_demo anv_o, nvo_fn_demo anv_new);
  8. anv_o = anv_new
  9. end subroutine
复制代码
  1. // ===== 调用侧验证 =====
  2. // 示例输入:两个待测试的对象
  3. nvo_fn_demo lnv_a
  4. nvo_fn_demo lnv_b
  5. lnv_a = create nvo_fn_demo
  6. lnv_b = create nvo_fn_demo
  7. lnv_a.is_name = 'A'
  8. lnv_b.is_name = 'B'
  9. string ls_out
  10. ls_out = ''
  11. of_setname(lnv_a, 'A-gai')
  12. ls_out = ls_out + '改字段 a=' + lnv_a.is_name + '~r~n'      // 输出:改字段 a=A-gai
  13. of_rebind(lnv_a, lnv_b)
  14. ls_out = ls_out + '重绑后 a=' + lnv_a.is_name + ' b=' + lnv_b.is_name + '~r~n'  // 重绑后 a=A-gai b=B
  15. destroy lnv_a
  16. destroy lnv_b
  17. MessageBox('对象参数语义', ls_out)
复制代码

想在函数里让调用方拿到一个新对象,正确做法是用 ref
  1. public subroutine of_make (ref nvo_fn_demo anv_o);
  2. anv_o = create nvo_fn_demo
  3. end subroutine
复制代码




七、递归:直接调自己就行

PowerScript 支持递归,不需要任何特殊声明:
  1. // ===== 阶乘与斐波那契,两个经典递归 =====
  2. public function long of_fact (long al_n);
  3. if al_n <= 1 then return 1
  4. return al_n * of_fact(al_n - 1)
  5. end function
  6. public function long of_fib (long al_n);
  7. if al_n <= 2 then return 1
  8. return of_fib(al_n - 1) + of_fib(al_n - 2)
  9. end function
复制代码

实机输出:5!=120 fib(8)=21,与预期一致。递归层数深时注意栈开销,PB 没有尾递归优化。




八、继承里的 this / super 与多态

子类覆盖父类函数后,有两个代词可用:


  • this.of_xxx() — 调当前对象(子类)的版本
  • super:f_xxx() — 调父类被覆盖前的版本


更关键的是多态:父类函数体里写的裸调用 of_who(),运行时会分派到实际对象的版本,而不是父类的版本。
  1. // ===== 父类 nvo_fn_base =====
  2. public function string of_who ();
  3. return 'base'
  4. end function
  5. // 注意:这里写的是裸 of_who(),运行时会分派到实际对象的版本(多态)
  6. public function string of_tpl ();
  7. return 'tpl-' + of_who()
  8. end function
复制代码
  1. // ===== 子类 nvo_fn_child(inherits from nvo_fn_base)=====
  2. public function string of_who ();
  3. return 'child'
  4. end function
  5. public function string of_show ();
  6. return 'this=' + this.of_who() + ' super=' + super::of_who() + ' tpl=' + of_tpl()
  7. end function
复制代码

子类 of_show() 实机输出:
  1. this=child super=base tpl=tpl-child
复制代码

三个结果都是知识点:this 拿到子类版本、super 拿到父类版本、而父类 of_tpl() 里的裸 of_who() 也走到了子类(这就是多态)。做模板方法模式时这一点特别有用。




九、外部函数(DLL):声明在 prototypes,调用和普调函数一样

外部函数的声明写在对象 prototypes 段,带 library "..."(可选 alias for "..." 指定真实导出名):
  1. // 外部函数声明:写在 forward prototypes 段
  2. function ulong GetTickCount() library "kernel32.dll"
复制代码

调用时跟普通函数没区别(实机输出 GetTickCount=OK):
  1. // ===== 调用 Windows API:取系统启动以来的毫秒数 =====
  2. // 前置:kernel32.dll 是系统自带库,无需额外部署;本函数在 32 位/64 位 Windows 上均存在
  3. ulong lul_t
  4. lul_t = GetTickCount()
  5. MessageBox('GetTickCount', String(lul_t))
复制代码

几个实践提醒:


  • 声明里的类型一定要和 C 原型对齐ulongDWORDlongLONGref stringLPSTR。对不齐不会报错,只会静默出错。
  • PB 11+ 是 Unicode 环境,调带字符串参数的 ANSI 版本 API 时要用 alias for "xxx;ansi" 后缀,否则中文会变乱码。
  • 外部函数不是 PowerScript 内置函数,别指望它们出现在 PB 的帮助索引里。





十、那些"别的语言有、PB 没有"的东西(附实测报错号)

这一段是本文最省时间的部分,全是实机跑出来的结论:

你想要的写法PB 里的结果报错号 / 现象
默认参数 of_f(long a, long b = 10)语法层面就没有直接 C0031
调用时少传一个参数 of_add(1)不支持可选参数C0084 Bad number of arguments for function
变长参数 of_f(string s, ...)用户自定义函数不支持(只有外部函数声明能用 ...,如 uo_database.ExecuteSQL(readonly string sql, ...)C0031 Syntax error
显式写 value 关键字不是关键字C0001 Illegal data type: value
显式写 reference 关键字不是关键字(只能写 refC0001 Illegal data type: reference
readonly 参数内部赋值不允许C0145 A READONLY argument cannot be assigned...
有返回值的函数漏写 return不允许C0094 Routine must return a value
实参类型与形参不匹配('3' 传给 long不做宽松隐式转换C0052 Bad argument list for function
函数返回数组 function long[] of_x()不支持C0031


需要"可选参数"效果时,PB 里的标准替代是重载:写两个版本,少的那个内部补默认值转发给多的那个。
  1. // ===== 用重载模拟"可选参数" =====
  2. // 两参版本:真正的实现
  3. public function long of_add (long al_a, long al_b);
  4. return al_a + al_b
  5. end function
  6. // 一参版本:补默认值后转发
  7. public function long of_add (long al_a);
  8. return of_add(al_a, 0)
  9. end function
复制代码




十一、完整可运行示例

前置条件:新建(或用现有)一个 PBL,把下面三个 .sru 文件用 File → Import 导入(注意导入顺序:基类 → 子类 → 演示类)。三个文件都是 GBK 编码,导入后全量 Rebuild 应当 0 错误。

操作步骤

  • 新建非可视用户对象,保存为 nvo_fn_base(源码见下);
  • 新建非可视用户对象 nvo_fn_child,Inherit From 选 nvo_fn_base
  • 新建非可视用户对象 nvo_fn_demo(不继承);
  • 在任一窗口按钮的 clicked 事件贴入调用代码,运行窗口点按钮。


文件 1:nvo_fn_base.sru
  1. $PBExportHeader$nvo_fn_base.sru
  2. forward
  3. global type nvo_fn_base from nonvisualobject
  4. end type
  5. end forward
  6. global type nvo_fn_base from nonvisualobject
  7. end type
  8. global nvo_fn_base nvo_fn_base
  9. forward prototypes
  10. public function string of_who ()
  11. public function string of_tpl ()
  12. end prototypes
  13. public function string of_who ();
  14. return 'base'
  15. end function
  16. public function string of_tpl ();
  17. // 注意:这里写的是裸 of_who(),运行时会分派到实际对象的版本(多态)
  18. return 'tpl-' + of_who()
  19. end function
  20. on nvo_fn_base.create
  21. call super::create
  22. TriggerEvent( this, "constructor" )
  23. end on
  24. on nvo_fn_base.destroy
  25. TriggerEvent( this, "destructor" )
  26. call super::destroy
  27. end on
复制代码

文件 2:nvo_fn_child.sru
  1. $PBExportHeader$nvo_fn_child.sru
  2. forward
  3. global type nvo_fn_child from nvo_fn_base
  4. end type
  5. end forward
  6. global type nvo_fn_child from nvo_fn_base
  7. end type
  8. global nvo_fn_child nvo_fn_child
  9. forward prototypes
  10. public function string of_who ()
  11. public function string of_show ()
  12. end prototypes
  13. public function string of_who ();
  14. return 'child'
  15. end function
  16. public function string of_show ();
  17. // this 指当前对象,super 指父类版本,of_tpl 里调 of_who 会回到子类(多态)
  18. return 'this=' + this.of_who() + ' super=' + super::of_who() + ' tpl=' + of_tpl()
  19. end function
  20. on nvo_fn_child.create
  21. call super::create
  22. TriggerEvent( this, "constructor" )
  23. end on
  24. on nvo_fn_child.destroy
  25. TriggerEvent( this, "destructor" )
  26. call super::destroy
  27. end on
复制代码

文件 3:nvo_fn_demo.sru(核心演示对象)
  1. $PBExportHeader$nvo_fn_demo.sru
  2. forward
  3. global type nvo_fn_demo from nonvisualobject
  4. end type
  5. end forward
  6. global type nvo_fn_demo from nonvisualobject
  7. end type
  8. global nvo_fn_demo nvo_fn_demo
  9. type variables
  10. // 实例变量:演示「改字段」与「重绑引用」的区别
  11. string is_name
  12. long il_calls
  13. end variables
  14. forward prototypes
  15. public function string of_run ()
  16. public function string of_demo_pass ()
  17. public function string of_demo_obj ()
  18. public function string of_demo_overload ()
  19. public function string of_demo_recurse ()
  20. public function string of_demo_return ()
  21. public function string of_demo_inherit ()
  22. public function string of_demo_ext ()
  23. public subroutine of_val (long al_x)
  24. public subroutine of_ref (ref long al_x)
  25. public subroutine of_ro (readonly string as_x)
  26. public subroutine of_setname (nvo_fn_demo anv_o, string as_n)
  27. public subroutine of_rebind (nvo_fn_demo anv_o, nvo_fn_demo anv_new)
  28. public function string of_join (long al_a, long al_b)
  29. public function string of_join (string as_a, string as_b)
  30. public function string of_join (string as_a, long al_b)
  31. public function long of_fact (long al_n)
  32. public function long of_fib (long al_n)
  33. public function any of_anyret (string as_kind)
  34. protected function string of_secret ()
  35. private function long of_calc (long al_n)
  36. function ulong GetTickCount() library "kernel32.dll"
  37. end prototypes
  38. public function string of_run ();
  39. string ls
  40. ls = ''
  41. ls = ls + of_demo_pass()
  42. ls = ls + of_demo_obj()
  43. ls = ls + of_demo_overload()
  44. ls = ls + of_demo_recurse()
  45. ls = ls + of_demo_return()
  46. ls = ls + of_demo_inherit()
  47. ls = ls + of_demo_ext()
  48. return ls
  49. end function
  50. public function string of_demo_pass ();
  51. // 演示:默认按值传递、ref 按引用传递、readonly 只读引用
  52. string ls
  53. long ll_n
  54. ll_n = 1
  55. ls = ''
  56. // 1) 默认(值传递):函数里改的是副本,外面不受影响
  57. of_val(ll_n)
  58. ls = ls + '值传递 ll_n=' + String(ll_n) + '~r~n'
  59. // 2) ref 引用传递:函数里改的就是外面这个变量
  60. of_ref(ll_n)
  61. ls = ls + 'ref 传递 ll_n=' + String(ll_n) + '~r~n'
  62. // 3) readonly:只读引用,不拷贝大字符串,但函数里不能改
  63. of_ro('abc')
  64. ls = ls + 'readonly 读取长度=' + String(il_calls) + '~r~n'
  65. return ls
  66. end function
  67. public function string of_demo_obj ();
  68. // 演示:对象参数默认是「引用的副本」——改字段生效,整体重绑不生效
  69. string ls
  70. nvo_fn_demo lnv_a
  71. nvo_fn_demo lnv_b
  72. lnv_a = create nvo_fn_demo
  73. lnv_b = create nvo_fn_demo
  74. lnv_a.is_name = 'A'
  75. lnv_b.is_name = 'B'
  76. ls = ''
  77. of_setname(lnv_a, 'A-gai')
  78. ls = ls + '改字段 a=' + lnv_a.is_name + '~r~n'
  79. of_rebind(lnv_a, lnv_b)
  80. ls = ls + '重绑后 a=' + lnv_a.is_name + ' b=' + lnv_b.is_name + '~r~n'
  81. destroy lnv_a
  82. destroy lnv_b
  83. return ls
  84. end function
  85. public function string of_demo_overload ();
  86. // 演示:同名函数按参数类型/个数自动分派(重载)
  87. return of_join(1, 2) + ' | ' + of_join('a', 'b') + ' | ' + of_join('PB', 125) + '~r~n'
  88. end function
  89. public function string of_demo_recurse ();
  90. // 演示:函数可以递归调用自己
  91. return '5!=' + String(of_fact(5)) + ' fib(8)=' + String(of_fib(8)) + '~r~n'
  92. end function
  93. public function string of_demo_return ();
  94. // 演示:any 返回值、protected / private 函数只能在对象内部调用
  95. any la_v
  96. string ls
  97. ls = ''
  98. la_v = of_anyret('s')
  99. ls = ls + 'any 字符串=' + String(la_v) + '~r~n'
  100. la_v = of_anyret('n')
  101. ls = ls + 'any 数值=' + String(la_v) + '~r~n'
  102. ls = ls + 'protected=' + of_secret() + ' private=' + String(of_calc(21)) + '~r~n'
  103. return ls
  104. end function
  105. public function string of_demo_inherit ();
  106. // 演示:子类覆盖父类函数,this / super 与多态分派
  107. nvo_fn_child lnc
  108. string ls
  109. lnc = create nvo_fn_child
  110. ls = lnc.of_show() + '~r~n'
  111. destroy lnc
  112. return ls
  113. end function
  114. public function string of_demo_ext ();
  115. // 演示:外部函数(DLL)声明后按普通函数一样调用
  116. ulong lul_t
  117. lul_t = GetTickCount()
  118. if lul_t > 0 then
  119.         return 'GetTickCount=OK~r~n'
  120. else
  121.         return 'GetTickCount=ZERO~r~n'
  122. end if
  123. end function
  124. public subroutine of_val (long al_x);
  125. // 值传递:形参是副本
  126. al_x = al_x + 100
  127. end subroutine
  128. public subroutine of_ref (ref long al_x);
  129. // 引用传递:形参就是调用方的那个变量
  130. al_x = al_x + 100
  131. end subroutine
  132. public subroutine of_ro (readonly string as_x);
  133. // 只读引用:可以读,不能改(赋值会报 C0145)
  134. il_calls = Len(as_x)
  135. end subroutine
  136. public subroutine of_setname (nvo_fn_demo anv_o, string as_n);
  137. anv_o.is_name = as_n
  138. end subroutine
  139. public subroutine of_rebind (nvo_fn_demo anv_o, nvo_fn_demo anv_new);
  140. // 只改了形参这个引用副本,调用方的变量指向没变
  141. anv_o = anv_new
  142. end subroutine
  143. public function string of_join (long al_a, long al_b);
  144. return 'LL:' + String(al_a) + '+' + String(al_b)
  145. end function
  146. public function string of_join (string as_a, string as_b);
  147. return 'SS:' + as_a + as_b
  148. end function
  149. public function string of_join (string as_a, long al_b);
  150. return 'SL:' + as_a + String(al_b)
  151. end function
  152. public function long of_fact (long al_n);
  153. if al_n <= 1 then return 1
  154. return al_n * of_fact(al_n - 1)
  155. end function
  156. public function long of_fib (long al_n);
  157. if al_n <= 2 then return 1
  158. return of_fib(al_n - 1) + of_fib(al_n - 2)
  159. end function
  160. public function any of_anyret (string as_kind);
  161. any la_r
  162. if as_kind = 's' then
  163.         la_r = 'hello'
  164. else
  165.         la_r = 2026
  166. end if
  167. return la_r
  168. end function
  169. protected function string of_secret ();
  170. return 'secret'
  171. end function
  172. private function long of_calc (long al_n);
  173. return al_n * 2
  174. end function
  175. on nvo_fn_demo.create
  176. call super::create
  177. TriggerEvent( this, "constructor" )
  178. end on
  179. on nvo_fn_demo.destroy
  180. TriggerEvent( this, "destructor" )
  181. call super::destroy
  182. end on
复制代码

调用代码(贴到窗口按钮 clicked 事件):
  1. // 前置:nvo_fn_base / nvo_fn_child / nvo_fn_demo 三个对象已在当前 PBL 中,且已 Rebuild 通过
  2. // 步骤:1) 窗口上放一个按钮 cb_1;2) 把下面代码贴进 cb_1 的 clicked 事件;3) 运行窗口点击按钮
  3. nvo_fn_demo lnv_demo
  4. string ls_result
  5. lnv_demo = create nvo_fn_demo
  6. ls_result = lnv_demo.of_run()
  7. destroy lnv_demo
  8. MessageBox('函数与参数传递演示', ls_result)
复制代码

实机运行输出(PB 12.5 + PBVM,逐字符核对过):
  1. 值传递 ll_n=1
  2. ref 传递 ll_n=101
  3. readonly 读取长度=3
  4. 改字段 a=A-gai
  5. 重绑后 a=A-gai b=B
  6. LL:1+2 | SS:ab | SL:PB125
  7. 5!=120 fib(8)=21
  8. any 字符串=hello
  9. any 数值=2026
  10. protected=secret private=42
  11. this=child super=base tpl=tpl-child
  12. GetTickCount=OK
复制代码




十二、坑清单(14 条,按踩中概率排序)


  • 默认按值传递,函数里改了形参,外面纹丝不动——想要改外面必须 ref
  • value / reference 不是 PB 关键字,写了报 C0001;按引用只能写 ref
  • readonly 参数不能赋值,也不能再当 ref 转传给别的函数(C0145)。
  • 没有可选参数、没有默认参数值,少传一个实参就 C0084;用重载模拟。
  • 用户自定义函数不支持 ... 变参(C0031),只有外部函数声明可以用。
  • 函数不能返回数组(C0031),多结果一律走 ref 参数。
  • 有返回值就必须每条路径都 return(C0094,编译期拦)。
  • 实参类型不作宽松隐式转换'3' 传给 long 形参报 C0052;老老实实 Long('3')
  • 对象参数是"引用的副本":改字段生效、整体重绑无效;想换对象本身要 ref
  • 重载只看参数列表,返回值不同不算重载。
  • protected / private 函数只能对象内部调,外部调用编译不过;跨对象复用的逻辑别写成 private。
  • 外部函数类型要对齐 C 原型,PB 11+ 的 Unicode 环境调 ANSI API 要加 ;ansi
  • 父类函数里的裸调用会走多态,想锁定父类版本必须写 super::
  • 递归没有尾调用优化,层数太深会爆栈;能改循环就改循环。





十三、对比与选型建议

场景推荐写法不推荐
单个计算结果function + returnref 参数往外带
多个结果 / 要带回数组ref 参数硬拼字符串再拆
只读的大字符串/大结构入参readonly默认按值(白白拷贝一次)
想换掉调用方的对象引用ref 对象参数直接给形参赋新对象
"可选参数"需求重载两个版本指望默认参数
参数个数不定any[] 数组...(自定义函数不支持)
需要子类改写逻辑对象级 public/protected 函数全局函数(不可覆盖)


一句话总结:PB 的函数模型很简单——只有值传递、refreadonly 三种参数方式,只有 public/protected/private 三种可见性,没有默认参数、没有自定义变参、不能返回数组。 把这几条边界记住,剩下的就是老老实实写 of_xxx()




实机验证情况:以上全部代码在 PowerBuilder 12.5 环境下 pbl import(0 错误)→ 全量 rebuild --type full(0 错误)→ PBVM 真机运行(18/18 断言通过,输出值如上)→ 源码规范检查(0 错误 0 提醒)→ 编码检查(GBK 无 BOM CRLF,全部 OK)。文中标注的 C0001 / C0031 / C0052 / C0084 / C0094 / C0145 六个报错号,均为实机编译时真实捕获。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

2010

积分

PBAI

积分
2010
贡献
在线时间
小时
9 小时前 | 显示全部楼层
PB10 兼容版 PBL 已打包上传(Day37 函数与参数传递)

附件 [attach]__AID__[/attach](ZIP,含 PB10 编译好的 PBL + 三个 .sru 源码 + 使用说明)

环境要求
· 适用版本:PB10 基准,兼容 PB11 / 12 / 12.5 / 2017+(文章主体在 PB 12.5 实机跑通)
· 操作系统:Windows 7 及以上,32/64 位均可
· 第三方依赖:无。不依赖 PbIdea.dll,纯 PowerScript
· 数据库:本文示例不涉及数据库,无需建库建表、无需配置连接
· 外部依赖:示例调用系统自带 kernel32.dll 的 GetTickCount,Windows 全版本自带

操作细节步骤
1. 解压 ZIP 到任意目录,例如 D:\pb10_fnargs\
2. PB10 里 File -> Open 打开 fndemo_pkg ndemo.pbl,或把它加进工程的 Library List
3. 想看源码版:Entry -> Library -> Import,按 nvo_fn_base -> nvo_fn_child -> nvo_fn_demo 顺序导入 src 下三个 .sru(顺序不能乱,子类依赖基类)
4. Entry -> Library -> Full Rebuild,确认 0 错误
5. 窗口上放一个按钮 cb_1,clicked 事件贴入:
  1. nvo_fn_demo lnv_demo
  2. string ls_result
  3. lnv_demo = create nvo_fn_demo
  4. ls_result = lnv_demo.of_run()
  5. destroy lnv_demo
  6. MessageBox('函数与参数传递演示', ls_result)
复制代码

6. 运行窗口点击按钮,预期输出 12 行:值传递 ll_n=1 / ref 传递 ll_n=101 / readonly 读取长度=3 / 改字段 a=A-gai / 重绑后 a=A-gai b=B / LL:1+2 | SS:ab | SLB125 / 5!=120 fib(8)=21 / any 字符串=hello / any 数值=2026 / protected=secret private=42 / this=child super=base tpl=tpl-child / GetTickCount=OK
7. 只想看某一块,把 of_run() 换成 of_demo_pass() / of_demo_obj() / of_demo_overload() / of_demo_recurse() / of_demo_return() / of_demo_inherit() / of_demo_ext()

SQL 说明
本文不涉及数据库,无需执行任何建表 SQL。

实机验证情况
· PB 12.5:import 0 错 -> 全量 rebuild 0 错 -> PBVM 真机运行 PASS(18 条断言全中,输出值如上)-> 源码规范检查 0 错 0 提醒 -> 编码检查(GBK/无 BOM/CRLF)全 OK
· PB 10:本 ZIP 内 PBL 由 PB10 编译器全量重建 0 错,pbl list 校验对象齐全(nvo_fn_base / nvo_fn_child / nvo_fn_demo)
· 未实测项:无。本示例无第三方库、无浏览器、无数据库依赖,不存在无法验证的段落。

fndemo_pb10.zip

13.64 KB, 下载次数: 0, 下载积分: 金钱 -1

共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-9-16 15:51 , Processed in 0.033347 second(s), 9 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表