上个月我接手一个老报表程序的性能优化,打开一个核心方法定义的时候,我盯着签名看了十秒钟,又翻到调用处,内心只有一句话:这到底传给了谁?方法上挂着两个字符串类型的 IMPORTING 参数,都带 DEFAULT,还标了一个 PREFERRED PARAMETER iv_key。调用方写的是 process( 'KEY_001' ),表面上人畜无害,但如果你没有把方法定义倒背如流,你根本不知道这个 KEY_001 是给了 iv_key 还是 iv_ctx。PREFERRED PARAMETER 这个语法,ABAP 官方文档一直没怎么重点宣传,但它在老代码里出现的频率比我预想的高得多,而且每次出现,几乎都在给后面的维护者埋雷。
这篇文章不打算写一个"全面禁用"的暴论。这个关键字确实有它的适用场景和存在价值,但以我这两年排查问题的经验来看,它被滥用的程度远超它带来的便利。核心问题在于:它把"调用代码的语义清晰度"牺牲掉了,换取的是"定义端的一次性便利",而且这笔交换在长期维护中往往亏本。今天从机制、可读性、可演进性、合理场景和替代方案五个角度,把这个语法聊透。
1. PREFERRED PARAMETER 最初想解决的问题
1.1 参数默认值解决不了的那类痛点
ABAP 方法调用一直有个老传统:IMPORTING 参数可以带 DEFAULT,调用方可以不传。这个设计本身没什么问题,但它有一个边界——当多个参数都带默认值时,调用方想只传"第二个参数",就必须把第一个也写出来,哪怕它只是占位。比如:
abap复制METHODS do_query
IMPORTING
iv_where TYPE string DEFAULT '1=1'
iv_fields TYPE string DEFAULT '*'.
如果调用方想只传 iv_fields,从语法上必须写成:
abap复制do_query( iv_where = '1=1'
iv_fields = 'EMPLOYEE_ID, NAME' ).
麻烦吗?确实麻烦。而且这种写法让调用代码变得啰嗦,尤其在参数位很多、调用点又密集的情况下,每个调用点都显式补一遍默认值,阅读负担一下子就上去了。PREFERRED PARAMETER 的初衷就是解决这个场景:它允许调用方只写一个参数,由编译器把未命名的实参按优先级匹配到指定参数上,从而省略掉那一堆"为了占位而存在的默认值"。
abap复制METHODS do_query
IMPORTING
iv_where TYPE string DEFAULT '1=1'
iv_fields TYPE string DEFAULT '*'
PREFERRED PARAMETER iv_fields.
这样一个调用就变得干净了:
abap复制do_query( 'EMPLOYEE_ID, NAME' ).
从语法设计角度来看,这个功能其实模仿了其他语言里"函数重载+默认参数"组合产生的效果——调用方只需要给出最关心的那个值,其余全部交给默认值兜底。
1.2 这个关键字真正改变的东西
必须承认,在非常有限的场景里,PREFERRED PARAMETER 是"最不坏"的选择。比如某个方法在演进过程中,新加入的第四个参数成为调用方最常自定义的入口,而老参数几乎全走默认值,为了不打断老调用点,用 PREFERRED PARAMETER 把新参数提出来,确实能在一段时间内保持调用面的整洁。
但问题也恰好出在这里。在 ABAP 这种强类型、半声明式的语言里,方法签名本身承担了一部分"文档"职责。调用方读代码时,通常会快速扫一眼 iv_ = ... 这种命名参数列表,来判断每个值去了哪里。PREFERRED PARAMETER 一旦出现,这种"命名即地址"的阅读方式就失灵了——你看到一个裸值,必须回到定义处确认它到底被匹配给了谁。
这还不是最要命的。更麻烦的是它的匹配规则并不像直觉上那么简单,多个字符串参数共存时的优先级、与位置参数混合使用时的行为、以及 DEFAULT 和 IS SUPPLIED 之间的交互,都会产生意想不到的边界情况。所以先把这个语法的底层机制拆开看,不然后面谈可读性和可演进性,都是空中楼阁。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 匹配机制拆解:调用方根本猜不到的规则
2.1 未命名实参与 PREFERRED 的优先关系
先确认一个基础行为:在 ABAP 中,调用方法时最安全的写法一定是命名参数——iv_name = value 这种。命名参数不存在歧义,无论签名怎么排,编译器都能精确定位。PREFERRED PARAMETER 影响的是"未命名实参"的匹配路径。
当一个调用写成这样:
abap复制process( 'HELLO' ).
如果方法签名里有多个 IMPORTING 参数,ABAP 编译器会按什么顺序去匹配这个 'HELLO'?答案是:在方法定义时用 PREFERRED PARAMETER 指定的那个参数拥有最高优先级,无论它在参数列表中的物理位置是第几个。
举个例子:
abap复制METHODS process
IMPORTING
iv_prefix TYPE string DEFAULT ''
iv_body TYPE string DEFAULT ''
iv_suffix TYPE string DEFAULT ''
PREFERRED PARAMETER iv_body.
此时调用 process( 'HELLO' ),编译器会把 'HELLO' 匹配到 iv_body,而不是第一个参数 iv_prefix。如果不看定义,你可能会以为 'HELLO' 传给了 iv_prefix——这个默认直觉在绝大多数 ABAP 方法里是成立的,但在这里被打破了。
更隐蔽的是,当调用方同时写了命名参数和未命名参数时,匹配优先级又会发生偏移。比如:
abap复制process( 'HELLO' iv_prefix = 'START' ).
这种情况下,未命名实参会继续去匹配 PREFERRED 参数,命名参数不受影响。如果 PREFERRED 参数已经被命名参数占用了,编译器会报错,还是把未命名实参匹配到下一个可用参数?不同 ABAP 版本的处理并不完全一致,这就是可移植性和可预测性上的一层阴影。
2.2 DEFAULT 与 PREFERRED 的组合效应
PREFERRED PARAMETER 和 DEFAULT 通常是一起出现的,因为如果 PREFERRED 参数本身是必填的,调用方只传一个裸值也能勉强工作;但如果它同时带 DEFAULT,调用方就有两种选择:不传,或传一个裸值。它们对应的是两条完全不同的调用路径。
看这个例子:
abap复制METHODS send_alert
IMPORTING
iv_title TYPE string DEFAULT 'Notification'
iv_message TYPE string DEFAULT ''
PREFERRED PARAMETER iv_message.
调用 A:
abap复制send_alert( 'Disk usage is high' ).
这个调用里,'Disk usage is high' 被匹配到 iv_message,iv_title 走默认值。语义上很清晰,虽然标题被忽略了,但消息内容生效。
调用 B:
abap复制send_alert( ).
这个调用什么都没传,两个参数都走默认值:标题是 'Notification',消息是空字符串。看起来也还行。
但坏就坏在,这两种调用在语法上都合法,而且语义差距很大,但从调用点去看,你很难一眼判断调用方到底是有意传了消息,还是仅仅漏传了参数。尤其是当一个方法有几个布尔型、字符串型、表型参数混在一起时,这种"漏传=默认值"的语义,很容易把 bug 掩盖掉。
2.3 与 IS SUPPLIED 检查的微妙关系
还有一个细节值得专门提一下:在方法内部,如果使用 IS SUPPLIED 来判断调用方是否显式传入了某个参数,PREFERRED PARAMETER 会让这个判断变得非常微妙。
abap复制METHOD send_alert.
IF iv_message IS SUPPLIED.
" 走定制消息逻辑
ELSE.
" 走默认消息逻辑
ENDIF.
ENDMETHOD.
当调用方写成 send_alert( 'oops' ),iv_message IS SUPPLIED 为真,这没问题。但如果调用方写的是 send_alert( iv_title = 'ALERT' ),此时 iv_message 没有被显式提供,IS SUPPLIED 为假,方法内部走默认逻辑。这个行为从机制上讲是合理的,但从调用方视角看,如果调用方本来以为"我没传消息就会用模板标题的默认逻辑",而方法内部却有另一套分支,这种不一致很容易埋下逻辑隐患。
所以我在分析和排查老代码时,只要方法签名里出现 PREFERRED PARAMETER,第一件事就是检查方法内部有没有 IS SUPPLIED 分支。有,就说明这个方法的控制流比表面看起来复杂得多。
3. 可读性代价:从"声明即文档"到"推理式阅读"
3.1 调用代码变得暧昧不清
ABAP 社区一直有一个好传统:方法调用尽量写成自解释的。iv_ 前缀、命名参数、语义化的参数名,这些约定保证了"读调用代码的人不需要频繁跳转到定义处"。但 PREFERRED PARAMETER 一出现,这个约定就被打破了。
对比一下两种写法:
改造前(普通命名参数):
abap复制lv_result = calculate_tax(
iv_amount = lv_net
iv_rate = lv_tax_rate
iv_round_flag = abap_true ).
改造后(使用 PREFERRED PARAMETER):
abap复制lv_result = calculate_tax( lv_net ).
从行数上看,后者确实省了。如果 calculate_tax 的语义是"默认按标准税率计算",这种简写也许可以接受。但问题在于,当方法参数多于两个且彼此作用差异很大时,calculate_tax( lv_net ) 这种写法就丢失了太多信息。读代码的人必须知道"第一个裸参数到底对应的是金额还是税率",而这是无法从调用点推断的。
我见过更极端的例子:一个方法有五个 IMPORTING 参数,其中三个是字符串,两个是整数,都带 DEFAULT,还挂了一个 PREFERRED PARAMETER 指向第三个字符串。调用方写 validate_config( 'TRUE' ),我不仅看不出 'TRUE' 给了谁,甚至连这个值应该匹配什么类型都要翻定义。这种代码的可读性,基本等同于把重要的业务逻辑埋在了一堆魔法值里。
3.2 搜索和静态分析全部失效
现代 ABAP 开发已经越来越依赖代码搜索和静态检查来理解代码结构。比如我在排查一个问题时,通常会全局搜索 iv_mode = 'X' 这样的调用模式,来确认哪些调用点显式传了 mode。但如果方法定义里写了 PREFERRED PARAMETER,部分调用点可能写成 call_method( 'X' ),这种调用点在搜索 iv_mode 时根本不会被命中,只有搜索 call_method 再加上人肉比对签名才能发现。
这种"关键参数在调用点不可见"的问题,对代码审查也是灾难。CR 的时候,评审人通常只扫变更的调用代码,他看到的是一堆裸值,根本无法判断这个值是否被正确路由到了目标参数。尤其是当 PREFERRED PARAMETER 指向的参数在签名里排位靠后时,调用点的裸值和目标参数之间隔着好几个默认参数,视觉上完全对不上。
3.3 对初级开发者尤其不友好
ABAP 团队里经常会混入刚转 SAP 的初级开发者,他们对语言规范往往依赖直觉。一个人的直觉是:方法调用里写的第一个值,应该传给参数列表里第一个位置。PREFERRED PARAMETER 把这个直觉打破了。于是初级开发者看到 process( 'HELLO' ),想当然地认为它传给了 iv_prefix,然后沿着这个错误理解去排查逻辑,结论自然跑偏。
这种认知成本很难量化,但确实是我在带人过程中反复遇到的痛点。一个团队里,如果 PREFERRED PARAMETER 用得多了,等于变相要求每一个新人都必须先通读一遍所有方法定义才能安全地读代码。这不是一个高效团队该有的状态。
4. 演进性陷阱:新增参数与签名重构的静默炸弹
4.1 新增一个参数,老调用语义全变
如果说可读性损失还能靠"勤查定义"来弥补,那可演进性上的坑是真正让人夜不能寐的。PREFERRED PARAMETER 最危险的地方在于:当你给一个方法新增 IMPORTING 参数时,如果顺手把它设成了新的 PREFERRED PARAMETER,老调用点的实参匹配结果会瞬间改变,而且编译器不会给出任何错误。
我构造一个简化案例:
方法原定义:
abap复制METHODS send_mail
IMPORTING
iv_to TYPE string
iv_subject TYPE string DEFAULT 'No Subject'
PREFERRED PARAMETER iv_to.
已有调用点:
abap复制send_mail( 'user@example.com' ).
这个调用工作正常:邮件发给 user@example.com,标题用默认值。
某天需求变更,要支持附件,开发者在签名里加了一个参数:
abap复制METHODS send_mail
IMPORTING
iv_to TYPE string
iv_subject TYPE string DEFAULT 'No Subject'
iv_attach TYPE string DEFAULT ''
PREFERRED PARAMETER iv_attach.
表面上看,老调用 send_mail( 'user@example.com' ) 语法依然合法,编译也通过。但此时 'user@example.com' 被匹配到了新参数 iv_attach,而 iv_to 变成了必填未传——如果 iv_to 没有 DEFAULT,这会在运行期或激活时直接报错;如果 iv_to 恰好也有 DEFAULT,那问题就更阴险了:程序不报错,但邮件发给了一个空地址或兜底地址,标题也是默认值,整个业务行为静默改变。
这种问题一旦上线,排查起来极其痛苦,因为代码层面看"所有调用点都还活着",语法零错误,肉眼可见的只是业务结果不对。
4.2 一次真实排查链路:报错为零,结果全错
去年我调过一个工单,现象是某个调度程序跑完后生成的文件是空的。第一反应是查文件写入逻辑,查半天没发现问题。后来把目光放到"生成文件名"这个方法上,它的定义如下:
abap复制METHODS build_filename
IMPORTING
iv_prefix TYPE string DEFAULT 'REPORT'
iv_ext TYPE string DEFAULT 'TXT'
PREFERRED PARAMETER iv_ext.
调用方写的是:
abap复制lv_name = build_filename( 'CSV' ).
这是个很常见的调用,意图明显是"把扩展名改成 CSV"。但在排查过程中我发现,某些配置场景下这个方法实际上是从一个内存表里读参数来调的,代码长这样:
abap复制lv_name = build_filename( ls_config-param ).
问题就出在这里:ls_config-param 在某个工单配置里恰好被填成了 REPORT_2024,而调用方本意是把它传给 iv_prefix。因为 PREFERRED PARAMETER 指向的是 iv_ext,这个值最终被当成了文件扩展名,文件名变成了 REPORT.TXT 还是 REPORT_2024?——是后者被拼到了扩展名里,文件系统里出现了一个名为 REPORT.REPORT_2024 的文件,后续流程按 REPORT.TXT 找文件,自然找不到,生成结果为空。
整个过程编译器零报错,静态检查也没抓出来。最后靠的是人肉对比"配置表内容"和"方法签名里的 PREFERRED 指向",才定位到根因。这种问题,本质上就是 PREFERRED PARAMETER 在配置驱动场景下,把"调用方意图"和"参数实际路径"之间撕开了一道裂缝。
4.3 重构时的隐性破坏
除了新增参数,日常重构里还有一个高频操作:重命名参数。在命名参数调用下,如果方法签名里的参数名改了,编译器会精准地报出所有老调用点,逼着你逐个去更新,这虽然麻烦,但至少安全。可一旦有 PREFERRED PARAMETER,老调用点可能根本没写参数名,方法内部引用的参数名一改,调用点依然活得好好的,编译零错误,但语义已经完全是另一回事了。
最让我头疼的是参数排序调整。ABAP 方法签名中参数的顺序本身不是必须稳定的,但如果有人调整了参数位置,而 PREFERRED PARAMETER 没有跟着改,老调用点里那些裸值的匹配目标就可能漂移到完全无关的参数上。这种"签名看着没变,行为却变了"的静默破坏,在 CI 不完善、没有足够单测覆盖的 ABAP 项目里,几乎等于裸奔。
PREFERRED PARAMETER 的破坏力不在于它本身有多复杂,而在于它把"参数绑定"从编译期可见的静态关系,变成了运行期才暴露歧义的动态关系。这是所有长期维护者最不愿意看到的东西。
5. 哪些场景确实值得用
写到这里,可能有人会觉得我在主张全面禁用。其实不是。这个关键字在某些特定场景下确实有它的价值,但使用前必须先过一道判断标准,而不是看到参数多就顺手挂一个。
5.1 真正适合用 PREFERRED PARAMETER 的场景
从我的经验看,能安全使用 PREFERRED PARAMETER 的场景通常满足以下条件:
- 方法只有一个"核心输入",其他参数都是边缘配置,且 95% 以上的调用方都只需要关心这个核心输入。
- 方法名本身已经能消除歧义,即使调用处只写一个裸值,读的人也能猜出它大概率给到了哪个参数。
- 方法被调用点数量有限,且团队内约定俗成"不写命名参数的调用一律靠查定义确认"。
- 该方法短期内没有新增参数的计划,且接口相对稳定。
典型例子是工厂类方法或转换类方法。比如:
abap复制METHODS convert_currency
IMPORTING
iv_amount TYPE p
iv_currency TYPE c LENGTH 5 DEFAULT 'CNY'
PREFERRED PARAMETER iv_amount.
这个场景里,convert_currency( 1200 ) 的语义非常清晰,裸值大概率就是金额,不存在第二个同样类型的候选参数。这种场景用 PREFERRED PARAMETER 是有收益的,调用代码确实简洁了,而且不会产生歧义。
5.2 一个可以落地的自检清单
我给自己定的规则是这样的,分享出来供参考:
- 参数列表里是否存在多个类型相近(比如两个以上字符串)的参数?如果是,不要用。
- 方法名是否能天然表达"裸参数"的意图?如果方法名本身不够具体,不要用。
- 调用方是否可能在不知道定义的情况下,从调用点读出正确语义?如果不能,不要用。
- 该方法是否属于核心业务链路上的高频方法?如果是,不要用。
- 改签名时是否能让编译器把所有老调用点都暴露出来?如果做不到,不要用。
只要以上任一条命中"不要用",就优先用普通命名参数 + DEFAULT 的方式,哪怕多写几行,也比埋一个隐式炸弹强。
5.3 历史代码里遇到 PREFERRED PARAMETER 怎么办
如果现在接手的老代码里已经有大量 PREFERRED PARAMETER,我的建议不是一口气全删掉,那会引起大规模回归风险。更稳妥的做法:
- 在调用点全部改成显式命名参数,这是第一步,消除所有裸值调用。
- 把所有调用点都完成命名化改造后,再移除方法定义里的 PREFERRED PARAMETER 关键字。
- 如果方法内部有 IS SUPPLIED 分支,改造前先确认调用点行为是否保持一致,防止改动后逻辑走向不同分支。
- 改造完成后的方法,用普通 IMPORTING + DEFAULT 表达,读代码的人只需要看命名参数列表就全明白了。
这套做法我在几个项目里用过,风险可控,而且对团队后续维护非常友好。关键在于:不要图快,先把调用点全部命名化,再动定义,顺序反了容易翻车。
6. 回归可读性的替代方案
6.1 用 OPTIONAL + IS SUPPLIED 代替隐式优先级
最直接的替代方案,就是老老实实用 OPTIONAL 参数 + DEFAULT 加 IS SUPPLIED 检查。不是所有参数都必须传,但不是所有"不传"都意味着同一个语义。在方法内部用 IS SUPPLIED 区分"调用方显式传了默认值"和"调用方根本没传",是更严谨的做法。
看这个例子:
abap复制METHODS process
IMPORTING
iv_mode TYPE string DEFAULT 'NORMAL'
iv_path TYPE string DEFAULT ''.
METHOD process.
IF iv_path IS SUPPLIED.
" 显式指定了路径,走完整逻辑
ELSE.
" 未指定路径,走简化逻辑
ENDIF.
ENDMETHOD.
调用方必须写成 process( iv_mode = 'BATCH' iv_path = '/tmp' ),每个值都清清楚楚。虽然多敲几个字符,但维护成本低得多。
6.2 参数对象:把多个关联参数收敛为一个结构
当方法参数太多且彼此关联时,与其纠结哪个参数做 PREFERRED,不如考虑把参数收敛成一个结构或对象。这是 ABAP 里经常被低估的重构手段。
改造前:
abap复制METHODS run_job
IMPORTING
iv_job_name TYPE string
iv_job_count TYPE i DEFAULT 1
iv_immediate TYPE abap_bool DEFAULT abap_true
iv_restart TYPE abap_bool DEFAULT abap_false.
调用点每次都要写一堆命名参数,或者干脆写成一串难看的裸值。
改造后:
abap复制TYPES: BEGIN OF ty_job_options,
job_name TYPE string,
job_count TYPE i,
immediate TYPE abap_bool,
restart TYPE abap_bool,
END OF ty_job_options.
METHODS run_job
IMPORTING
is_options TYPE ty_job_options.
调用点变成:
abap复制run_job( VALUE #(
job_name = 'DAILY_REPORT'
job_count = 3
immediate = abap_true ) ).
这种方式的可读性提升是质的飞跃:所有参数一目了然,而且后续新增参数不会破坏现有调用点,因为结构字段的兼容性比位置参数强得多。对于那种"参数一多就想用 PREFERRED"的冲动,参数对象往往是更好的答案。
6.3 方法拆分:别让一个方法承接太多职责
有时候参数多本身就是一个信号:这个方法做的事情可能太多了。与其在调用语法上做文章,不如把方法拆开。
比如原来有个 save_entity 方法,接收主数据、校验规则、日志开关、归档标志、通知名单等一堆参数,调用方每次都要传四五个命名参数。重构时可以拆成 save_entity(只做核心保存)、save_entity_with_validation、save_entity_with_notification 等细粒度方法,每个方法签名只有必要的两三个参数。这样调用点自然干净,也不需要 PREFERRED PARAMETER 这种语法糖。
方法拆分对可演进性的帮助特别大:新需求来了,要么新增一个变体方法,要么直接改对应的小方法,而不是在一个大方法签名上不断堆参数。签名稳定了,PREFERRED PARAMETER 的用武之地也就没有了。
6.4 实在要兼容老调用时怎么办
有一种场景,已经有一套对外发布的接口方法,老的调用方很多,不能强制他们一次性改完,这时候确实需要临时兼容。我的建议是:保留原方法,但不要用 PREFERRED PARAMETER 来"隐形重载"。可以在原方法内部做一个转发层:
abap复制METHODS send_mail
IMPORTING
iv_to TYPE string
iv_subject TYPE string DEFAULT 'No Subject'
iv_attach TYPE string DEFAULT ''.
METHOD send_mail.
" 老逻辑,直接使用命名参数访问
ENDMETHOD.
METHODS send_mail_with_attach
IMPORTING
iv_to TYPE string
iv_subj TYPE string
iv_attach TYPE string.
新调用方用 send_mail_with_attach,老调用方继续用 send_mail,两者并存一段时间,等老调用点全部迁移后再废弃老方法。这个过程中,PREFERRED PARAMETER 一点都不需要登场。
6.5 方案对比
| 方案 | 可读性 | 演进安全性 | 适用场景 |
|---|---|---|---|
| 普通 IMPORTING + DEFAULT | 高:调用点全命名参数,语义自解释 | 高:新增/重命名参数时编译器能暴露老调用点 | 大多数日常方法 |
| PREFERRED PARAMETER | 低:裸值需要靠定义反推 | 低:新增/重排参数可能静默改变匹配结果 | 极少数核心输入极其明显、签名长期稳定的场景 |
| 参数对象 | 高:字段名自带语义 | 很高:结构字段兼容性优于位置参数 | 参数多且彼此关联的业务对象 |
| 方法拆分 | 最高:方法职责单一,签名天然精简 | 最高:变更局部化 | 参数多且可以按职责划分的方法 |
从表里可以明显看出,PREFERRED PARAMETER 在三个维度上都处于劣势,唯一的优势只是"少打字"。在代码维护和团队协作的语境下,"少打字"的权重,远抵不上可读性和可演进性上的损失。
7. 让 ABAP 方法调用回归"显式即安全"
我在这篇文章里反复强调的其实是同一个观点:方法调用的可读性,不能依赖"读者愿意翻定义"来兜底。真正可靠的代码,是签名本身就把意图写清楚。send_mail( iv_to = lv_addr iv_subject = lv_title ) 这样的调用,任何人接过来都能直接改;send_mail( lv_addr ) 这种,哪怕定义处挂了 PREFERRED PARAMETER,也要多花几秒钟去确认、去心算,而且每一步心算都可能出错。
PREFERRED PARAMETER 这个语法,本身不是不能用,但它把一项本应在编译期确定的"参数绑定关系"变成了需要人肉确认的隐式约定。这个代价,在只有两三个人的小项目里也许还能承受,在几十人协作、方法被跨模块复用的企业级环境里,就是持续流血的伤口。
我个人的习惯是:写新方法时,默认不用 PREFERRED PARAMETER;遇到老代码里用了的,先看它是否命中前面说的合理场景,命中则保留,不命中则列入重构清单,按"调用点命名化优先、定义移除其次"的顺序逐步清除。
最后说一个实在的小技巧:如果你为了兼容老调用而不得不临时保留 PREFERRED PARAMETER,那么在方法定义处加一行注释,写明"此参数为兼容旧调用而保留,新代码请使用命名参数",至少能给后面接手的同事一个方向。这种注释,我称之为"善意的最小成本"。它虽然不能根治问题,但能让维护者少走很多弯路。
