这样的事情(是的,这不涉及一些边缘情况 - 这不是重点):
int CountDigits(int num) { int count = 1; while (num >= 10) { count++; num /= 10; } return count; }
您对此有何看法?也就是说,使用函数参数作为局部变量
两者都被放置在堆栈上,并且性能非常相似,我想知道这方面的最佳实践方面
当我为int numCopy = num
组成的函数添加一条额外且相当冗余的行时,我觉得自己像个白痴,但它确实让我感到烦恼。
你怎么看?这应该避免吗?
答案 0 :(得分:16)
作为一般规则,我不会使用函数参数作为本地处理变量,即我将函数参数视为只读。
在我看来,直观地理解代码对于可维护性是至关重要的,并且修改函数参数以用作本地处理变量趋向于以与该目标相反地运行。我已经开始期望一个参数在方法的中间和底部具有与顶部相同的值。 另外,一个恰当命名的本地处理变量可能会提高可理解性。
然而,正如@Stewart所说,这条规则或多或少取决于函数的长度和复杂程度。对于像你所展示的简单函数这样的简单函数,简单地使用参数本身可能比引入一个新的局部变量(非常主观)更容易理解。
然而,如果我要编写像countDigits()
这样简单的东西,我倾向于使用remainingBalance
本地处理变量来代替修改num
参数作为本地的一部分处理 - 对我来说似乎更清楚。
有时,我会修改方法开头的局部参数以规范化参数:
void saveName(String name) {
name = (name != null ? name.trim() : "");
...
}
我认为这是可以的,因为:
一个。在方法的顶部很容易看到,
湾参数保持其原始概念意图,
℃。该参数对于方法的其余部分是稳定的
然后,有一半时间,我只是倾向于使用局部变量,只是为了在那里得到一些额外的final
(好吧,这是一个不好的理由,但我喜欢{ {1}}):
final
如果99%的时间,代码保留未修改的函数参数(即,此代码库的变异参数不直观或意外),那么,在其他1%的时间内,在长/复杂函数的顶部放弃关于变异参数的快速评论可能是可理解性的一大福音:
void saveName(final String name) {
final String normalizedName = (name != null ? name.trim() : "");
...
}
P.S。 :-)
参数与参数
http://en.wikipedia.org/wiki/Parameter_(computer_science)#Parameters_and_arguments
这两个术语有时可以互换使用;特别是,“论证”有时用来代替“参数”。然而,存在差异。正确地,参数出现在过程定义中;参数出现在过程调用中。
所以,
int CountDigits(int num) {
// num is consumed
int count = 1;
while (num >= 10) {
count++;
num /= 10;
}
return count;
}
int foo(int bar)
是一个参数。
bar
int x = 5
int y = foo(x)
的值是x
参数的参数。
答案 1 :(得分:9)
当我这样做时,对我来说总觉得有点滑稽,但这并不是一个避免它的好理由。
可能可能希望避免它的一个原因是出于调试目的。当你进行调试的一半时,能够分辨出“暂存器”变量和函数输入之间的区别非常有用。
我不能说在我的经历中经常出现非常的东西 - 通常你会发现只是为了拥有一个不同的名称而引入另一个变量是值得的/ em>,但如果最干净的代码最终会改变变量的值,那么就这样吧。
这个可以出现并且完全合理的一种情况是你有一些价值意味着“使用默认值”(通常是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
}
答案 2 :(得分:4)
关于C和C ++ :
我的观点是使用参数作为函数的局部变量很好,因为它已经 一个局部变量。为什么不这样使用呢?
将参数复制到新的局部变量中时,我觉得很傻,只是为了使用一个可修改的变量。
但我认为这几乎是个人观点。按你的喜好去做。如果你因为这个而感觉复制参数,它表明你的个性不喜欢它,那你就不应该这样做。
答案 3 :(得分:1)
如果我不需要原始值的副本,我不会声明一个新变量。
IMO我认为一般来说,改变参数值并不是一种坏习惯 这取决于你将如何在代码中使用它。
答案 4 :(得分:0)
我的团队编码标准建议不要这样做,因为它可能会失控。在我看来,你所展示的功能就像你所展示的一样,它没有受到伤害,因为每个人都可以看到发生了什么。问题是随着时间的推移,函数变得更长,并且它们会在其中修复错误。一旦一个函数不止一个屏幕充满代码,这就开始变得混乱,这就是为什么我们的编码标准禁止它。
编译器应该能够很容易地摆脱冗余变量,因此它没有效率影响。可能只是你和你的代码审查员之间是否合适。
答案 5 :(得分:0)
我通常不会更改函数中的参数值。如果在函数后面的某个时刻你需要引用原始值,你仍然可以使用它。在你的简单情况下,没有问题,但如果你以后添加更多的代码,你可以参考'num'而不会意识到它已被更改。
答案 6 :(得分:0)
代码需要尽可能自给自足。我的意思是你现在依赖于作为算法一部分传入的内容。如果您团队中的其他成员决定将其更改为引用传递,那么您可能会遇到大问题。
如果您希望它们是不可变的,那么最佳做法肯定是复制入站参数。
答案 7 :(得分:0)
我通常不修改函数参数,除非它们是指针,在这种情况下我可能会改变指向的值。
答案 8 :(得分:0)
我认为最好的做法因语言而异。例如,在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 at距离。这里最好的做法是在返回数据之前复制数据*,或者将其设置为只读**。
* 在Perl中,请参阅内置Storable模块中的函数dclone()
。
** 在Perl中,请参阅内置Hash::Util模块中的lock_hash()
或lock_hash_ref()
。