【问题标题】:Using function arguments as local variables使用函数参数作为局部变量
【发布时间】:2011-02-14 18:13:47
【问题描述】:
类似这样的事情(是的,这不涉及某些极端情况——这不是重点):
int CountDigits(int num) {
int count = 1;
while (num >= 10) {
count++;
num /= 10;
}
return count;
}
您对此有何看法?也就是说,使用函数参数作为局部变量。
两者都放在堆栈上,并且性能几乎相同,我想知道这方面的最佳实践方面。
当我向包含int numCopy = num 的函数添加额外且相当多余的行时,我感觉自己像个白痴,但是它确实困扰了我。
你怎么看?应该避免这种情况吗?
【问题讨论】:
标签:
local-variables
function-parameter
【解决方案1】:
-
作为一般规则,我不会将函数参数用作本地处理变量,即我将函数参数视为只读。
在我看来,直观易懂的代码对于可维护性至关重要,而修改函数参数以用作本地处理变量往往与该目标背道而驰。我已经开始期望参数在方法的中间和底部具有与顶部相同的值。 另外,一个恰当命名的本地处理变量可能会提高可理解性。
不过,正如@Stewart 所说,这条规则或多或少的重要取决于函数的长度和复杂性。对于像您展示的那样简短的简单函数,简单地使用参数本身可能比引入新的局部变量更容易理解(非常主观)。
尽管如此,如果我要编写像 countDigits() 这样简单的东西,我倾向于使用 remainingBalance 本地处理变量来代替修改 num 参数作为本地处理的一部分 - 看起来更清楚我。
-
有时候,我会在方法的开头修改一个局部参数来规范化参数:
void saveName(String name) {
name = (name != null ? name.trim() : "");
...
}
我认为这没关系,因为:
一个。在方法的顶部很容易看到,
b.该参数保持其最初的概念意图,并且
c。该参数对于该方法的其余部分是稳定的
再一次,有一半的时间,无论如何我都喜欢使用局部变量,只是为了在那里获得几个额外的finals(好吧,这是一个不好的理由,但我喜欢final ):
void saveName(final String name) {
final String normalizedName = (name != null ? name.trim() : "");
...
}
-
如果在 99% 的情况下,代码未修改函数参数(即,对于此代码库,变异参数是不直观或意外),那么,在其他1% 的情况下,在长/复杂函数的顶部放一个关于变异参数的快速评论可能会大大提高可理解性:
int CountDigits(int num) {
// num is consumed
int count = 1;
while (num >= 10) {
count++;
num /= 10;
}
return count;
}
附: :-)
参数与参数
http://en.wikipedia.org/wiki/Parameter_(computer_science)#Parameters_and_arguments
这两个术语有时可以随意互换使用;特别是,有时使用“参数”代替“参数”。尽管如此,还是有区别的。正确地,参数出现在过程定义中;参数出现在过程调用中。
所以,
int foo(int bar)
bar 是一个参数。
int x = 5
int y = foo(x)
x 的值是bar 参数的实参。
【解决方案2】:
当我这样做时,我总是觉得有点好笑,但这并不是避免它的好理由。
您可能可能想要避免它的一个原因是出于调试目的。当您调试到一半时,能够分辨“暂存器”变量和函数输入之间的区别会非常有用。
我不能说这是我的经验中经常出现的东西非常 - 而且你经常会发现值得引入另一个变量只是为了拥有不同的名称,但如果最干净的代码最终改变了变量的值,那就这样吧。
这种可以出现并且完全合理的一种情况是,您有一些值意味着“使用默认值”(通常是 Java 或 C# 等语言中的空引用)。在那种情况下,我认为将参数的值修改为“真实”默认值是完全合理的。这在 C# 4 中特别有用,可以有可选参数,但默认值必须是常量:
例如:
public static void WriteText(string file, string text, Encoding encoding = null)
{
// Null means "use the default" which we would document to be UTF-8
encoding = encoding ?? Encoding.UTF8;
// Rest of code here
}
【解决方案3】:
关于 C 和 C++:
我的观点是,使用参数作为函数的局部变量很好,因为它已经是一个局部变量。那为什么不直接使用呢?
当我将参数复制到一个新的局部变量中只是为了有一个可修改的变量可以使用时,我也觉得很傻。
但我认为这几乎是个人意见。随心所欲地做。如果你因此觉得复制参数很无聊,说明你的性格不喜欢它,你不应该这样做。
【解决方案4】:
如果我不需要原始值的副本,我不会声明新变量。
IMO 我不认为改变参数值通常是一种不好的做法,
这取决于您将如何在代码中使用它。
【解决方案5】:
我的团队编码标准建议不要这样做,因为它可能会失控。在我看来,对于您展示的功能,这并没有什么坏处,因为每个人都可以看到正在发生的事情。问题是随着时间的推移,函数会变得更长,并且它们会修复错误。一旦一个函数的代码超过一屏,这就会让人感到困惑,这就是我们的编码标准禁止它的原因。
编译器应该能够很容易地摆脱冗余变量,因此它不会影响效率。这可能只是你和你的代码审查者之间的事。
【解决方案6】:
我一般不会更改函数内的参数值。如果稍后在函数中需要引用原始值,您仍然拥有它。在您的简单情况下,没有问题,但如果您稍后添加更多代码,您可能会在没有意识到它已更改的情况下引用“num”。
【解决方案7】:
代码需要尽可能自给自足。我的意思是你现在依赖于作为算法的一部分传入的内容。如果您团队的其他成员决定将其更改为通过引用传递,那么您可能会遇到大问题。
如果您希望入站参数是不可变的,那么最好的做法肯定是复制入站参数。
【解决方案8】:
我通常不会修改函数参数,除非它们是指针,在这种情况下我可能会更改指向的值。
【解决方案9】:
我认为最佳实践因语言而异。例如,在 Perl 中,您可以 localize 任何变量甚至变量的一部分到本地范围,这样在该范围内更改它不会对它之外产生任何影响:
sub my_function
{
my ($arg1, $arg2) = @_; # get the local variables off the stack
local $arg1; # changing $arg1 here will not be visible outside this scope
$arg1++;
local $arg2->{key1}; # only the key1 portion of the hashref referenced by $arg2 is localized
$arg2->{key1}->{key2} = 'foo'; # this change is not visible outside the function
}
有时我会因为忘记本地化通过引用传递给函数的数据结构而烦恼,我在函数内部进行了更改。相反,我还返回了一个数据结构作为函数结果,该结果在多个系统之间共享,然后调用者错误地继续更改数据,从而在通常称为 action 的难以跟踪的问题中影响这些其他系统一段距离。最好的做法是在返回数据之前克隆数据*,或将其设为只读**。
* 在 Perl 中,查看内置 Storable 模块中的函数 dclone()。
** 在 Perl 中,请参阅内置 Hash::Util 模块中的 lock_hash() 或 lock_hash_ref()。