PowerBuilder 数据类型与类型转换全解:标准类型、ANY、NULL 三态与精度溢出(PB12.5 实机验证)
PowerBuilder 数据类型与类型转换全解:标准类型、ANY、NULL 三态与精度溢出(PB12.5 实机验证)阅读说明
1. 适用版本:PB 10 / PB 12.5(示例以 PB10 写法为基准,全部代码在 PB 12.5 实机编译并运行通过;版本差异处单独标注)
2. 支持数据库:本文为纯 PowerScript 语言主题,不依赖任何数据库
3. 操作系统与环境要求:Windows 7 及以上,装有 PB 10 或 PB 12.5 开发环境/运行库;无需第三方组件
4. 难度系数:★★☆☆☆
5. 其它阅读说明:建议先了解变量声明与作用域;文中所有"实测"输出值均取自 PB12.5 真机运行,不是推测
一、为什么类型这件事总在半夜出事
写 PowerBuilder 的人,大概都遇到过这几种场景:金额累加了几万行之后差了几分钱;一个返回 -5536 的数量怎么算都不对;界面上明明没填值,if ls_name = '' then 却死活不进;Integer('12.9') 拿到的不是 13 而是 12,还一声不响。
这些问题没有一个是"语法不会",全都是类型的边界行为没搞清。PowerScript 是一门弱声明、强隐式规则的语言:它不要求你到处写类型转换,但它内部的转换规则又不完全符合直觉。本文把这套规则一次讲透——标准类型有哪些、转换函数怎么选、转换失败会怎样、NULL 到底怎么参与运算、精度与溢出在哪里崩、隐式转换哪些能哪些不能。
下面所有输出值,都是把代码做成 NVO 对象、用 PB12.5 真机编译后在 PBVM 里跑出来的实测结果(共 80 条硬断言,全部命中)。
二、标准数据类型全景
先给一张全景表,后面每一节都是这张表里某一格的展开。
类型说明典型用途备注
boolean真/假开关、标志位String(true) 得到小写 true
integer(int)2 字节整数行号、序号、小计数器上限 32767,最容易被撑爆
unsignedinteger(uint)2 字节无符号端口、编码值实测 65535 正常
long4 字节整数日常默认整数类型不确定就用 long
unsignedlong(ulong)4 字节无符号句柄、大计数实测 4294967295 正常
longlong8 字节整数超过 21 亿的计数实测 12345678901 正常
real4 字节单精度浮点兼容老代码精度低,新代码别用
double8 字节双精度浮点科学计算、非金额小数有二进制误差
decimal(dec)十进制精确小数金额、单价、税率0.1+0.2 严格等于 0.3
char单字符取字符、ASCII 处理与 string 可直接互赋
string变长字符串文本中文按字符计长度
blob变长二进制图片、文件、加密结果见 Day26 的 UpdateBlob 篇
date日期生日、单据日期1000-01-01 ~ 3000-12-31
time时间时刻精度到微秒
datetime日期+时间时间戳不能直接赋给 date
any万能容器通用参数、异构数据取用时要自己知道装了什么
上表的字节数与取值范围是 PB 帮助文档的常规说明;本文实机验证的是行为(溢出、回绕、转换、比较),见后文各节。凡标注"实测"的,都是真机跑出来的数字。
一句话选型:整数用 long,小数分两种——金额一律 decimal,非金额用 double;字符串和数值之间永远显式转换。
三、声明、默认值:变量声明出来不是 NULL
这是最容易被 C/Java 习惯带偏的一条。PowerScript 里变量声明后自动初始化,数值是 0、字符串是空串,不是 NULL。实测:long ll_zero 声明后 IsNull(ll_zero) = false,值 = 0。
NULL 只有你主动 SetNull() 或从数据库读入空列时才会出现。
前置:新建一个窗口 w_type_demo,放一个按钮 cb_1,把下面代码贴进 cb_1 的 clicked 事件;运行窗口点击按钮即可看到结果。
// 示例输入:演示用的数值(可自行修改观察变化)
long ll_zero
long ll_x
string ls_s
string ls_ret
// 声明后不赋值:PowerScript 自动初始化为 0 / 空串,不是 NULL
ll_zero = 0
ls_ret = '数值默认 IsNull=' + String(IsNull(ll_zero))
ls_ret = ls_ret + '; 默认值=' + String(ll_zero)
// 字符串同样:声明后是空串,不是 NULL
ls_s = ''
ls_ret = ls_ret + '~r~n字符串默认 IsNull=' + String(IsNull(ls_s))
ls_ret = ls_ret + '; 长度=' + String(Len(ls_s))
// 只有主动 SetNull 才是 NULL
SetNull(ll_x)
ls_ret = ls_ret + '~r~nSetNull 后 IsNull=' + String(IsNull(ll_x))
MessageBox('默认值与 NULL', ls_ret)
四、显式类型转换:函数清单与实测返回值
PowerScript 的转换函数都是"类型名当函数名",非常好记。
函数作用实测例子结果
Integer(s)转 2 字节整数Integer('123')123
Long(s)转 4 字节整数Long('1234567890')1234567890
Dec(s)转 decimalDec('123.456')123.456
Double(s)转双精度Double('1.5e3')1500(支持科学计数法)
Real(s)转单精度Real('3.25')3.25
String(x [,mask])转字符串,可带格式掩码String(1234.5678,'#,##0.00')1,234.57
Date(s)转日期Date('2026-09-18')2026-09-18
Time(s)转时间Time('14:30:15')14:30:15
DateTime(d, t)日期+时间合成 datetimeDateTime(Date('2026-09-18'), Time('14:30:15'))2026-09-18 14:30:15
IsNumber(s)能不能当数字IsNumber(' 12 ')true(前后空格允许)
IsDate(s)能不能当日期IsDate('不是日期')false
IsNull(x) / SetNull(x)判空 / 置空——
注意:转换函数不接受变量作为目标,它是"返回值",必须自己接。
前置:同上,代码贴在 cb_1 的 clicked 事件。
// 示例输入:待转换的原始字符串(都按"用户手工输入"来设想)
string ls_num
string ls_date
string ls_time
// 示例输入:用于演示结果的目标变量
long ll_v
dec ldec_v
date ld_v
datetime ldt_v
string ls_ret
ls_num = '1234567890'
ls_date = '2026-09-18'
ls_time = '14:30:15'
// 字符串 -> 数值
ll_v = Long(ls_num)
ldec_v = Dec('123.456')
// 字符串 -> 日期 / 时间 / 时间戳
ld_v = Date(ls_date)
ldt_v = DateTime(ld_v, Time(ls_time))
ls_ret = 'Long=' + String(ll_v)
ls_ret = ls_ret + '~r~nDec=' + String(ldec_v)
ls_ret = ls_ret + '~r~nDouble(科学计数法)=' + String(Double('1.5e3'))
ls_ret = ls_ret + '~r~nDate=' + String(ld_v)
ls_ret = ls_ret + '~r~nDateTime=' + String(ldt_v)
MessageBox('显式转换', ls_ret)
五、转换失败不报错 —— 这条坑最深
PowerScript 的类型转换失败不抛异常、不返回 NULL,而是返回"零值"。 实测结果:
代码返回值说明
Integer('12.9')12直接截断,不是四舍五入
Integer('abc')0与"用户真的填了 0"无法区分
Integer('')0同上
Long('abc')0
Dec('abc')0
Double('abc')0
Date('不是日期')1900-01-01不是空日期,是 1900 年!
而且 IsNull(String(Integer('abc'))) 实测 = false —— 结果真的就是 0,不是 NULL,所以你没法用判空来兜底。
正确做法:转换前先用 IsNumber / IsDate 校验。注意 IsNumber(' 12 ') 返回 true(前后空格容忍),IsNumber('12.5.6') 返回 false。
前置:同上。
// 示例输入:用户从界面填进来的原始串(脏数据,故意给各种形态)
string ls_in
string ls_ret
// 演示一:直接转,看失败的"零值"
ls_in = 'abc'
ls_ret = "Integer('abc')=" + String(Integer(ls_in))
ls_ret = ls_ret + "~r~nInteger('12.9')=" + String(Integer('12.9'))
ls_ret = ls_ret + "~r~nDate('不是日期')=" + String(Date('不是日期'))
// 演示二:先校验再转(推荐写法)
ls_in = '12.5'
if IsNumber(ls_in) then
ls_ret = ls_ret + '~r~n校验通过,Dec=' + String(Dec(ls_in))
else
ls_ret = ls_ret + '~r~n非法数字:' + ls_in
end if
ls_in = '12.5.6'
if IsNumber(ls_in) then
ls_ret = ls_ret + '~r~n校验通过'
else
ls_ret = ls_ret + '~r~n非法数字(多个小数点):' + ls_in
end if
// 演示三:日期同样先 IsDate
ls_in = '2026-09-18'
if IsDate(ls_in) then
ls_ret = ls_ret + '~r~n合法日期=' + String(Date(ls_in))
else
ls_ret = ls_ret + '~r~n非法日期:' + ls_in
end if
MessageBox('转换失败与校验', ls_ret)
六、NULL 三态:既不是真也不是假
NULL 在 PowerScript 里是第三态,参与任何运算/比较的结果还是 NULL,而 if 遇到 NULL 一律按假处理,走 else 分支。实测:
表达式实测结果
IsNull(已置空的变量)true
NULL变量 + 1结果是 NULL(IsNull = true)
if NULL变量 = 1 then走 else(cmp=F)
if IsNull(NULL变量) then走 then(ifnull=T)
String(NULL数值)返回 NULL(不是空串)
'a' + NULL字符串整串变 NULL
NULL字符串 = ''false(NULL 不等于空串)
Len(NULL字符串)返回 NULL
最后两条合起来就是最著名的 NULL 传染:把 NULL 拼进返回串,整串变 NULL,写文件就写出 0 字节——排查时表现为"程序跑成功了但输出是空的"。
防御写法:凡是可能为空的值,拼串前过一遍 IsNull。
前置:同上。
// 示例输入:模拟从数据库读出来的、可能为空的字段
long ll_qty
string ls_memo
string ls_ret
string ls_part
// 模拟"数据库里是 NULL"
SetNull(ll_qty)
SetNull(ls_memo)
// 一、判空必须用 IsNull,不能用 = ''
if IsNull(ll_qty) then
ls_ret = '数量为空(NULL)'
else
ls_ret = '数量=' + String(ll_qty)
end if
// 二、NULL 参与运算,结果还是 NULL
if IsNull(ll_qty + 1) then
ls_ret = ls_ret + '~r~nNULL+1 仍然是 NULL'
else
ls_ret = ls_ret + '~r~nNULL+1=' + String(ll_qty + 1)
end if
// 三、拼串前先消毒,否则整串变 NULL(写文件就是 0 字节)
ls_part = String(ls_memo)
if IsNull(ls_part) then
ls_part = '(空)'
end if
ls_ret = ls_ret + '~r~n备注=' + ls_part
// 四、NULL 不等于空串,别混为一谈
if ls_memo = '' then
ls_ret = ls_ret + '~r~n备注是空串'
else
ls_ret = ls_ret + '~r~n备注不是空串(它是 NULL)'
end if
MessageBox('NULL 三态', ls_ret)
七、ANY:什么都能装,但你要自己记住装了什么
any 是 PowerScript 的万能容器,可以装数值、字符串、日期、布尔甚至对象。实测 any la 依次装入 3 / 'abc' / 3.14 / Date('2026-09-18') / true / 12345678901,String(la) 全部能正确取出。
两个要点:
[*]取用时没有类型检查——你装的是日期却当数值算,编译器不会拦你,结果在运行时才炸。
[*]想动态知道装了什么,用 ClassName(any变量)。实测装入字面量 3 后 ClassName(la) 返回 long(注意:整数字面量在 PB 里按 long 处理,不是 integer)。
前置:同上。
// 示例输入:要依次装进 any 的各种值(模拟异构参数)
any la_v
string ls_ret
// 装数值
la_v = 3
ls_ret = '装 3 -> ' + String(la_v) + ',ClassName=' + ClassName(la_v)
// 装字符串
la_v = 'abc'
ls_ret = ls_ret + '~r~n装 abc -> ' + String(la_v)
// 装小数
la_v = 3.14
ls_ret = ls_ret + '~r~n装 3.14 -> ' + String(la_v)
// 装日期
la_v = Date('2026-09-18')
ls_ret = ls_ret + '~r~n装日期 -> ' + String(la_v)
// 装布尔
la_v = true
ls_ret = ls_ret + '~r~n装 true -> ' + String(la_v)
// 装超过 long 范围的大整数(字面量本身会按 longlong 处理)
la_v = 12345678901
ls_ret = ls_ret + '~r~n装大整数 -> ' + String(la_v)
MessageBox('ANY 变量', ls_ret)
any 适合做"万能参数"(比如一个通用日志接口),不适合做业务变量。能用确定类型就别用 any,否则把编译期错误推成了运行期错误。
八、精度与溢出:钱要用 decimal,数要用 long
这一节的三条实测结论,每一条都能单独救一次火。
1)double 有二进制误差,decimal 没有
double:0.1 + 0.2 = 0.3 但(0.1+0.2 = 0.3) 判定为 F(假)
decimal: 0.1 + 0.2 = 0.3 且(0.1+0.2 = 0.3) 判定为 T(真)
String() 显示出来都是 0.3,但比较结果完全不同。所以金额比较绝对不能用 =,要么用 decimal,要么用容差比较。
2)integer 相乘,赋给谁决定会不会溢出(这条最反直觉)
long ll = li_a * li_b →60000 (li_a=300, li_b=200)
integer li = li_a * li_b →-5536 (同一个表达式!)
integer li = 300 * 200 →-5536 (字面量一样溢出)
long ll = long(li_a) * li_b → 60000 (先提升就没事)
原因:表达式的结果类型受赋值目标影响,目标是 integer 时按 2 字节算,60000 直接回绕成 -5536。写代码时中间结果一律用 long 承接。
3)加减溢出静默回绕
integer li = 32767; li = li + 1 实测得到 -32768,没有任何提示。
4)整除要自己截断
/ 是普通除法:7 / 2 实测 = 3.5。要整除用 Int() 或 Truncate()(实测 Int(7/2) = 3),取余用 Mod(7,2) = 1。
前置:同上。
// 示例输入:参与计算的原始数据
dec ldec_a
dec ldec_b
double ld_a
double ld_b
integer li_a
integer li_b
integer li_c
long ll_p
string ls_ret
// 一、金额用 decimal:0.1+0.2 严格等于 0.3
ldec_a = 0.1
ldec_b = 0.2
ls_ret = 'decimal: ' + String(ldec_a + ldec_b)
if ldec_a + ldec_b = 0.3 then
ls_ret = ls_ret + ' (等于 0.3:真)'
else
ls_ret = ls_ret + ' (等于 0.3:假)'
end if
// 二、非金额用 double,但比较要留容差
ld_a = 0.1
ld_b = 0.2
ls_ret = ls_ret + '~r~ndouble : ' + String(ld_a + ld_b)
if ld_a + ld_b = 0.3 then
ls_ret = ls_ret + ' (等于 0.3:真)'
else
ls_ret = ls_ret + ' (等于 0.3:假 —— 二进制误差)'
end if
// 三、integer 乘法:赋给 long 正确、赋给 integer 溢出
li_a = 300
li_b = 200
ll_p = li_a * li_b
li_c = li_a * li_b
ls_ret = ls_ret + '~r~n300*200 给 long=' + String(ll_p) + ',给 integer=' + String(li_c)
ll_p = long(li_a) * li_b
ls_ret = ls_ret + '~r~n先提升再乘=' + String(ll_p)
// 四、加减溢出静默回绕
li_c = 32767
li_c = li_c + 1
ls_ret = ls_ret + '~r~n32767+1=' + String(li_c)
// 五、除法是普通除法,整除要自己截断
ls_ret = ls_ret + '~r~n7/2=' + String(7 / 2) + ',Int(7/2)=' + String(Int(7 / 2)) + ',Mod(7,2)=' + String(Mod(7, 2))
// 六、舍入三兄弟
ls_ret = ls_ret + '~r~nRound(1234.5678,2)=' + String(Round(1234.5678, 2))
ls_ret = ls_ret + ',Truncate=' + String(Truncate(1234.5678, 2))
ls_ret = ls_ret + ',Int(1234.987)=' + String(Int(1234.987))
ls_ret = ls_ret + ',Ceiling(1234.1)=' + String(Ceiling(1234.1))
MessageBox('精度与溢出', ls_ret)
九、隐式转换:哪些自动,哪些必须显式
PowerScript 只在数值族内部和字符/字符串之间做自动转换,其余一律要你显式写。下面这些是实测的编译结果:
写法是否通过说明
long = integer变量✅ 自动小范围整数提升为大范围
string = char变量✅ 自动实测 'A' 转出长度 1
date -> datetime(DateTime(ld, lt))✅ 需函数不能直接赋值
datetime = date❌ C0008类型不匹配,必须 DateTime(ld, Time('00:00:00'))
'n=' + 数值变量❌ C0011字符串不能直接拼数值,必须 String()
if '123' = 123 then❌ C0011字符串与数值不能比较
if 布尔变量 = 整数变量 then❌ C0003 / C0011布尔与数值不能比较
第一条(字符串拼数值)是新手最高频的编译错误。记住:只要往字符串里塞数值,就写 String()。
前置:同上。
// 示例输入:用于演示提升与转换的原始值
integer li_small
long ll_big
char lc_ch
string ls_s
date ld_d
datetime ldt_dt
string ls_ret
// 一、integer 自动提升为 long
li_small = 5
ll_big = li_small
ls_ret = 'integer->long: ' + String(ll_big)
// 二、char 自动转 string
lc_ch = 'A'
ls_s = lc_ch
ls_ret = ls_ret + '~r~nchar->string: ' + ls_s + ',长度=' + String(Len(ls_s))
// 三、date 合成 datetime(不能直接赋值,必须走 DateTime())
ld_d = Date('2026-09-18')
ldt_dt = DateTime(ld_d, Time('00:00:00'))
ls_ret = ls_ret + '~r~ndate->datetime: ' + String(ldt_dt)
// 四、字符串拼数值必须显式 String()
ls_ret = ls_ret + '~r~n拼数值: n=' + String(ll_big)
// 五、布尔转字符串是小写
ls_ret = ls_ret + '~r~nString(true)=' + String(true)
MessageBox('隐式转换', ls_ret)
十、String() 格式化掩码
String(值, 掩码) 是 PowerScript 里最实用的输出工具,实测掩码效果:
掩码代码实测输出
#,##0.00String(1234.5678, '#,##0.00')1,234.57
0.00%String(0.256, '0.00%')25.60%(自动乘 100)
000String(7, '000')007
yyyy-mm-ddString(日期, 'yyyy-mm-dd')2026-09-18
yyyy年mm月dd日String(日期, 'yyyy年mm月dd日')2026年09月18日
yyyy-mm-dd hh:mm:ssString(时间戳, 掩码)2026-09-18 14:30:15
0.00E+00String(12345.6789, '0.00E+00')1.23E+04
#,##0.00;(#,##0.00)String(-1234.5, 掩码)(1,234.50)(负数用括号)
前置:同上。
// 示例输入:待格式化的金额(千分位演示用)
dec ldec_amt
// 示例输入:待格式化的时间戳
datetime ldt_now
// 示例输入:百分比、补零、负数等演示值
string ls_ret
ldec_amt = 1234.5678
ldt_now = DateTime(Date('2026-09-18'), Time('14:30:15'))
// 掩码里的 # 表示"有则显示、无则不补",0 表示"没有也补 0"
ls_ret = '千分位: ' + String(ldec_amt, '#,##0.00')
ls_ret = ls_ret + '~r~n百分比: ' + String(0.256, '0.00%')
ls_ret = ls_ret + '~r~n补零: ' + String(7, '000')
ls_ret = ls_ret + '~r~n日期: ' + String(Date('2026-09-18'), 'yyyy年mm月dd日')
ls_ret = ls_ret + '~r~n时间戳: ' + String(ldt_now, 'yyyy-mm-dd hh:mm:ss')
ls_ret = ls_ret + '~r~n科学计数: ' + String(12345.6789, '0.00E+00')
ls_ret = ls_ret + '~r~n负数括号: ' + String(-1234.5, '#,##0.00;(#,##0.00)')
MessageBox('格式化掩码', ls_ret)
顺带一提字符与 ASCII 的互转:Asc('A') 实测 = 65,Char(65) = A;中文同样有效,Asc('中') = 20013,Char(20013) = 中;Asc('') 返回 0(不是 NULL)。
十一、DataWindow / 数据库类型与 PowerScript 类型对照
做数据窗口时经常要"数据库里的这一列,在 PowerScript 里该用什么接":
数据库类型(SQL Server)DataWindow 列类型PowerScript 取数类型
intnumberlong
bigintnumberlonglong
smallint / tinyintnumberinteger / long
decimal(p,s) / numericdecimaldecimal(金额必选)
float / realnumberdouble
char / varchar / textstringstring
datetime / smalldatetimedatetimedatetime
date / time(2008+)date / timedate / time
image / varbinary(max)blobblob(走 UpdateBlob/SelectBlob)
bitbooleanboolean
要点:GetItemDecimal() 取金额列、GetItemNumber() 取普通数值列、GetItemString() 取字符列、GetItemDateTime() 取时间戳列。用错取值函数不是报错,而是悄悄丢精度或丢时间部分。
十二、常见疑问 Q&A
Q1:integer 和 int 有区别吗?
没有,int 是 integer 的简写;同理 uint/ulong、dec/decimal。声明时两种写法等价,建议团队统一一种。
Q2:为什么我写 long ll_x = Long('123') 编译不过?
PowerScript 不允许在声明行的同时用表达式初始化。必须拆两行:先 long ll_x,再 ll_x = Long('123')。这条规则同样适用于 uo_json lj = create uo_json 这类对象声明——必须写成 uo_json lj 然后 lj = create uo_json。
Q3:dec 需要指定小数位数吗?
不像数据库那样需要声明精度。PowerScript 的 decimal 是变长十进制类型,你赋多少位它就保留多少位;真正决定显示位数的是 String() 的掩码或 DataWindow 列的 Format 属性。
Q4:判断两个金额是否相等,最稳妥怎么写?
用 decimal 类型承载,然后直接 =;如果数据源是 double,改用容差:if Abs(ld_a - ld_b) < 0.000001 then。
Q5:把一个 any 传进函数,形参该写什么类型?
形参也写 any 就行,PowerScript 不做类型检查。代价是函数内部必须自己 ClassName() 判断装的是什么,否则运行时才会出问题——这也是不建议在业务代码里滥用 any 的原因。
Q6:String(日期) 不写掩码会怎样?
按系统区域设置的短日期格式输出,同一段程序在不同机器上可能得到 2026-09-18 或 2026/9/18。凡是落库、导出、拼接报文的日期,一律显式写掩码。
十三、坑清单(14 条,都是实测踩出来的)
[*]转换失败返回 0 不是 NULL:Integer('abc') = 0,IsNull 判不出来,必须先 IsNumber。
[*]Integer('12.9') = 12:截断不是四舍五入,要四舍五入先转 Dec 再 Round。
[*]Date('非法') = 1900-01-01:这个日期会真的写进数据库,比 NULL 更难查。
[*]IsNumber(' 12 ') = true:前后空格容忍,但 '12.5.6' 是 false。
[*]NULL 拼进字符串,整串变 NULL:写文件就是 0 字节,表现为"程序成功但输出为空"。
[*]String(NULL) 返回 NULL,不是空串;Len(NULL串) 也返回 NULL。
[*]NULL 不等于空串:if ls = '' then 对 NULL 不成立,判空一律用 IsNull。
[*]NULL 参与比较结果是 NULL,if 当假处理:if NULL变量 = 1 永远走 else。
[*]double 的 0.1+0.2 ≠ 0.3:显示是 0.3,比较是假;金额用 decimal。
[*]integer 乘法会不会溢出,看赋值给谁:long 接 60000、integer 接 -5536。
[*]integer 加减溢出静默回绕:32767+1 = -32768,无提示。
[*]字符串不能直接拼数值:'n=' + li_i 编译报 C0011,必须 String()。
[*]datetime 不能直接赋给 date:报 C0008,用 DateTime(ld, Time('00:00:00'))。
[*]Blob() 不能接数值类型:实测 Blob(integer变量) 在 PB12.5 里编译不通过(导入阶段直接失败),Blob() 只用于 string/blob。
十四、选型对比与小结
场景推荐别用原因
金额、单价、税率decimaldoubledouble 有二进制误差,累计会差分
科学计算、坐标doublerealreal 精度太低
计数、行号、主键longintegerinteger 上限 32767 太容易爆
超过 21 亿longlonglonglong 上限约 21 亿
通用参数any—但取用务必自己记住类型
判空IsNull()= '' / = 0NULL 与空串/0 是两回事
数值转字符串String(x, 掩码)直接 +直接拼编译不过
一句话收尾:PowerScript 的类型规则不复杂,难的是它"不报错"。转换失败给 0、溢出给回绕、NULL 给传染——三件事都不会抛异常。把这三处的防御写进习惯(先 IsNumber/IsDate 校验、中间结果用 long/decimal 承接、拼串前过 IsNull),能省掉绝大多数半夜事故。
附:本文实机验证环境
[*]PB 12.5(pypower + PBORCA 导入,build rebuild --type full 0 错误)
[*]验证方式:把全部演示做成一个 nvo_type_demo 对象,在 PBVM 中运行并落盘输出,做 80 条硬断言,全部命中
[*]静态检查:check_pb125 0 错误 0 提醒;check_enc 编码/行尾/源码头全部 OK
[*]文中所有"实测"数字均来自该次真机运行,未实测的部分已明确标注
本文的 PB10 兼容版示例已打包成附件,可直接下载跑。
%AID%
一、环境要求
· PowerBuilder 10 及以上(本 PBL 用 --pb-version 100 全量编译,0 错误;PB 11/12/12.5 可直接用)
· Windows 7 及以上,装好 PB10 运行时(PBVM100.DLL)
· 不需要数据库:本文是纯 PowerScript 语言主题,示例不涉及连库与建表
· 不需要任何第三方组件
二、操作细节步骤
1. 下载附件 typeddemo_pb10.zip,解压到本地任意目录,例如 D:\pb10_typedemo\
2. 打开 PB10,选中你的目标 PBL,右键 Import...,选择 src\nvo_type_demo.sru
3. 导完后库里会出现对象 nvo_type_demo(11 个 public function)
4. 执行 Full Build,预期 0 错误 0 警告
5. 在任意按钮的 clicked 事件里写下面这段,运行即可看到全部演示结果:
nvo_type_demo lnv
string ls_ret
lnv = create nvo_type_demo
ls_ret = lnv.of_run()
destroy lnv
MessageBox('结果', ls_ret)
只想看某一组就单独调用对应方法,例如 lnv.of_null() 只看 NULL 三态、lnv.of_precision() 只看精度与舍入。
三、你会看到的实测输出(PB12.5 真机)
· OVERFLOW:int_target_mul=-5536(赋给 integer)/ intmul_bad=60000(赋给 long)/ intmax1=-32768
· CONVERTFAIL:Integer('12.9')=12、Integer('abc')=0、Date('非法')=1900-01-01
· NULL:zero_isnull=false(声明出来不是 NULL)、str=<NULL>(String(NULL) 返回 NULL)、cat_isnull=true(拼串传染)
· PRECISION:dbleq=F(double 的 0.1+0.2 ≠ 0.3)而 deceq=T(decimal 严格相等)
· FORMAT:num=1,234.57、pct=25.60%、pad=007、sci=1.23E+04
· CHAR:asc=65、asc_cn=20013、asc_empty=0
四、实机验证情况
· PB12.5:import 0 错 + 全量编译 0 错 + PBVM 运行 PASS(80 条硬断言全中)+ check_pb125 0 错 0 提醒 + check_enc bad=0
· PB10:--pb-version 100 全量编译 0 错,pbl list 对象齐全
· 无未实测部分:示例不含数据库、第三方组件与界面交互,所有代码路径都已真机执行
五、几个最容易踩的点
· 'n=' + 数值变量 会报 C0011,必须写 String(数值变量)
· 300*200 赋给 integer 得 -5536,赋给 long 得 60000,中间结果一律用 long 接
· 转换失败不报错只返回 0,判不出来,必须先 IsNumber / IsDate
· NULL 拼进字符串会让整串变 NULL,写文件就是 0 字节,拼之前先 IsNull
页:
[1]