方法中的赋值调用不良做法?

Assignment in method call bad practice?

提问人:Java Jive 提问时间:2/26/2018 更新时间:3/26/2018 访问量:316

问:

这是我向 Stackoverflow 提出的第一个问题,尽管我已经是多年的消费者。如果我违反规则,请原谅我。这当然不是我的本意。我已经仔细审查了这些规则,并相信我在可接受的范围内。但是,如果存在使用错误,请您指出这些错误,以便我将来更加合规。

我教高中生编程。这学期我们正在学习 C#。上周我们研究了递归。我分配了几个可以用递归解决的经典问题,其中之一就是幂。

我的一个学生提交了以下代码作为使用递归的幂运算解决方案(他确实允许我在这里发布它)。方法调用中的任务给了我威利斯,但当我告诉他这是不好的做法时,他抗议说“它有效”,对他来说“有意义”,而且他“一直都在这样做”。

static void Recur(int n1, int n2, int n3) 
{ 
   if (n2 > 0) 
   { 
      Recur(n1, n2 - 1, n3 *= n1);   // this is the line in question
   } 
   else 
   { 
      Console.WriteLine("The number calculated recursively is: {0}", n3); 
   } 
} 

我很难想出一些具体的东西来告诉我的学生,为什么在方法调用中进行作业通常是不好的做法,除了 1) 意外副作用的可能性,以及 2) 维护的困难。

我在网上搜索了关于这个问题的每一个短语,但结果却是空手而归。我确实看到了一本名为“Clean Code”的书,作者是Robert C. Martin,但我不拥有。

我和学生的关系有点像父母的关系。他们有时不会对我所说的话投入太多精力,除非我能用独立消息来源证实。如果我能指出一个关于将作业放在方法调用中的明确声明,我的学生会更倾向于停止这种烦人的习惯。

这种用法会打扰其他人吗?我是否期望他改变他做事的方式?他今年15岁,但前途一片光明。他是那些“明白”的学生之一。我不希望他养成不良习惯。

感谢您的时间和意见。

C# 方法 值运算符 副作用

评论

3赞 Matthew Watson 2/26/2018
在这种情况下,它是没有意义的,因为值 of 在被更改后没有被使用——而无意义的代码就是噪音,而噪音是坏的。即使它被使用,那么它被更改的位置也被埋在方法调用中,使其难以被发现,这也是不好的。(请注意,什么都不做但使代码更易于理解的代码并非毫无意义,而且是可以的。n3
2赞 Ron Beyer 2/26/2018
这应该在 CodeReview.SE 上提出,因为它是关于风格而不是损坏的代码。
0赞 Ron Beyer 2/26/2018
我想说的是,在方法调用中进行赋值并不一定是坏事,只要清楚发生了什么。然而,应该像瘟疫一样避免递归。这是一项很好的理解技能,但主要是为了避免它。如果你正在教递归,我会说下一课是关于在没有递归的情况下做同样的事情,并讨论为什么你会选择一个而不是另一个。
0赞 Matthew Watson 2/26/2018
补充我之前的评论:告诉你的学生这也行得通,所以没有必要。Recur(n1, n2 - 1, n3 * n1);=
0赞 Kevin Fichter 2/26/2018
与其寻找一个可引用的来源来指出,为什么不帮助他证明为什么这是一个糟糕的设计呢?例如,做一个强制溢出的测试,并向他展示调试器在何处以及如何抱怨他的代码,而不是你喜欢的“标准”递归代码(尾部或正常)。然后以某种方式更改规范(这是软件,客户端总是更改规范),并要求重构。哪一个更容易重构?最后,我认为重要的是,他要理解为什么他选择这种递归方式而不是更熟悉的递归方式:如果你不能捍卫一个代码选择,那么你真的理解它吗?

答:

4赞 InBetween 2/26/2018 #1

在发布的代码中有很多可以改进的地方:

  1. 保持一致

    为什么不打电话?为什么会受到不同的对待?这可能会在审查/维护代码时造成混淆。Recur(n1, n2 =- 1, n3 *= n1)n3

  2. 不要做不必要或多余的工作

    之后是否使用过?不?那为什么要浪费时间分配一个从未使用过的值呢?n3Recur(n1, n2 - 1, n3 *= n1)

  3. 不改变方法参数被认为是很好的做法(当然,除非它们通过引用传入)。为什么?因为它使调试和理解代码的工作方式变得更加困难;初始条件在方法执行时发生变化,使得跟踪可能的错误、优化、改进等变得更加困难。

总而言之,我避免使用这些模式,因为我的记忆力很差:

var i = 0;
Foo(i += 1);
Bar(i);

传递到什么?是还是是?我不记得了,我每次都要查。审查此代码的人可能会遇到同样的问题。这些聪明的技巧不会使代码工作得更快或更好,并且它避免了总共一行代码......不值得。Foo01

0赞 mentallurg 3/26/2018 #2

错误的代码是这样的:您的学生没有看到任何问题,因为方法参数没有。只要定义它们,你就会看到这种和平的代码会导致编译错误。n3 *= n1finalfinal

为什么要使用?因为这样可以确保方法参数不会在方法内用于任何其他目的。这使得代码更不容易出错,更可靠。将一个变量用于一个目的也会使代码更容易被其他人理解,这在行业中很重要。final

正如其他人已经指出的那样,为 n3 赋值是没有意义的,因为代码中没有使用变量 n3 的进一步。

他声称这“对他来说很有意义”,这表明他不在现实生活中,在行业中进行软件开发 - 从小公司开始,到谷歌或Facebook。其中一个要求是代码应该是可维护的。这种代码的和平不是。

关于“前途光明”:你怎么知道的?他赢得过任何编程比赛吗?他关于他“一直这样做”的论点是相当薄弱的。问他为什么要这样做?给他一个关于代码质量的小研究。也许这会帮助他稍微了解一下软件开发。