将返回值的计算和return语句拆分为单行方法?


26

我曾与一位同事讨论过如何中断一条return语句以及该语句在两行中计算返回值。

例如

private string GetFormattedValue()
{
    var formattedString = format != null ? string.Format(format, value) : value.ToString();
    return formattedString;
}

代替

private string GetFormattedValue()
{
    return format != null ? string.Format(format, value) : value.ToString();
}

在代码方面,我在第一个变量中看不到任何值。对我来说,后者更清晰,尤其是对于较短的方法。他的论点是,前一种变体更易于调试-这是一个很小的优点,因为当由于断点而停止执行时,VisualStudio允许我们对语句进行非常详细的检查。

我的问题是,是否仍然需要编写不太清晰的代码,只是为了使调试一目了然?拆分计算和语句后该变体还有其他参数return吗?


18
在VS上不起作用,但是假设您不能在复杂的表达式上设置条件断点(或者输入起来会很复杂),因此为了方便起见,可能会将assign和return放在单独的语句中。无论如何,编译器很可能会提出相同的代码。
tofro

1
这可能取决于语言,尤其是在变量具有(可能是复杂的)对象行为而不是指针行为的语言中。@Paul K的语句可能适用于具有指针行为的语言,对象具有简单值行为的语言以及具有成熟,高质量编译器的语言。
MSalters

4
“由于Visual Studio允许我们非常详细地检查语句,当由于断点而停止执行时”-就是这样。那么,如果函数返回的结构包含多个成员,如何获得返回值?(并且对该功能的支持充其量是零散的,有很多组合根本无法获得返回值)。
Voo

2
在某人正在使用TODAY的调试器中,如何对语句进行“非常详细的检查”,这使编写代码成为一个不好的选择,因此很容易在任何调试器中进行调试?
伊恩

16
通过将整个功能体缩减为private string GetFormattedValue() => string.Format(format ?? "{0}", value);
Graham

Answers:


46

引入解释变量是众所周知的重构,有时可以帮助使复杂的表达式更易读。但是,在所示的情况下,

  • 附加变量不会“解释”周围方法名称中不清楚的任何内容
  • 该语句变得更长,因此(略)不易读

而且,在大多数情况下,较新版本的Visual Studio调试器可以显示函数的返回值,而无需引入多余的变量(但是请注意,有一些警告,请参阅此较早的SO文章和不同的答案)。

因此,在这种特定情况下,我同意您的看法,但是在其他情况下,解释变量确实可以提高代码质量。


我也同意,毫无疑问,在某些情况下它很有用。
Paul Kertscher

2
我通常使用result变量的名称。没有那么长,而且更容易调试
edc65

26
@ edc65:像这样的通用名称result 通常只会增加代码的噪音,并且很少增加可读性,这正是我的答案。可以在有助于调试的上下文中证明这一点是合理的,但是在使用不需要单独变量的调试器时,我会避免使用。
Doc Brown

6
@JonHanna长期以来一直以我为荣。该名称result传达的信息是这是函数产生的值,因此您可以在函数返回之前先进行查看。
edc65

1
@ edc65,但这使它看起来很有用。因此,当我阅读您的代码时,我不会立即意识到事实并非如此。因此,您的代码变得不那么可读。
乔恩·汉娜

38

鉴于以下事实:

a)由于编译器优化了变量,因此对最终代码没有影响。

b)将其分开可以增强调试能力。

我个人得出的结论是,99%的时间将它们分开是一个好习惯。

这样做没有实质性的缺点。关于膨胀代码的说法是一个错误的称呼,因为膨胀代码与不可读或难以调试的代码相比是一个琐碎的问题。此外,此方法本身不能创建混乱的代码,这完全取决于开发人员。


9
这对我来说是正确的答案。这样可以更轻松地设置断点并在调试时查看值,并且没有我所知道的缺点。
马修·詹姆斯·布里格斯

对于点b,在Visual Studio代码中,只需将断点放在返回值上,然后添加表达式:GetFormattedValue()即可,当命中断点时将显示结果,因此不需要多余的行。但是,由于不需要在调试器中添加任何其他表达式,因此更容易看到带有额外行的本地人。因此,确实是个人喜好问题。
乔恩·雷诺

3
@JonRaynor作为返回值,将断点放在函数的右括号中。即使在具有多个返回的函数中,它也会捕获返回的值。
Baldrickk

16

通常,引入变量只是为了命名一些结果,这在使代码更具自记录性时非常有用。在这种情况下,这不是一个因素,因为变量名称与方法名称非常相似。

请注意,单行方法没有任何内在价值。如果更改引入了更多行但使代码更清晰,那将是一个很好的更改。

但总的来说,这些决定在很大程度上取决于您的个人喜好。例如,我发现这两个解决方案都令人困惑,因为不必要地使用了条件运算符。我本来希望有一个if语句。但是在您的团队中,您可能已经同意了不同的约定。然后按照您的约定进行操作。如果约定在这样的情况下保持沉默,请注意,这是一个非常小的更改,从长远来看并不重要。如果这种模式反复发生,请启动讨论您作为一个团队如何处理这些情况的讨论。但这在“好的代码”和“也许稍微更好的代码”之间扯上了界限。


1
“我发现这两种解决方案都令人困惑,因为不必要地使用了条件运算符。” -这不是一个真实的例子,我只需要快速地做出一些弥补。诚然,这可能不是最好的例子。
Paul Kertscher

4
从本质上讲这是“不为人知的”区别而+1(在其他条件相同的情况下),不值得为之困扰。
TripeHound

3
@Mindwin,当我使用三元运算符时,我通常将其拆分为多行,以便弄清楚什么是正确的情况,什么是错误的情况。
Arturo TorresSánchez17年

2
@ArturoTorresSánchez我也这样做,但是我使用a 和- 而不是a ?:if() {} else {\\ :)
Mindwin

3
@Mindwin,但是当我处于表达式中间(如对象初始化程序)时,我无法做到这一点
Arturo TorresSánchez17年

2

针对您的问题:

我的问题是,是否仍然需要编写不太清晰的代码,只是为了使调试一目了然?

是。 实际上,在我看来,您先前的声明的一部分(没有冒犯)有些短视(请参阅下面的粗体)“ 他的论点是,前一种变体更易于调试- 这是一个很小的优点,因为VisualStudio当由于断点而停止执行时,我们可以对语句进行非常详细的检查。

使调试更加方便(几乎)从来没有“的小功绩 ”,因为有人估计的程序员的50%的时间都花费在调试(可逆调试软件)。

使用split计算和return语句的变体是否还有其他参数?

是。 一些开发人员认为,拆分计算更容易阅读。当然,这有助于调试,但在有人试图破译代码可能执行或应用的任何业务规则时也有帮助。

注意:由于业务规则可以经常更改,因此可以更好地在数据库中提供服务。尽管如此,在这一领域进行清晰的编码仍然至关重要。(如何构建业务规则引擎


1

我会更进一步:

private string GetFormattedValue()
{
    if (format != null) {
        formattedString = string.Format(format, value);
    } else {
        formattedString = value.ToString()
    }
    return formattedString;
}

为什么?

将三元运算符用于更复杂的逻辑将是不可读的,因此对于更复杂的语句,您将使用上述样式。通过始终使用这种样式,您的代码是一致的,并且对于人类来说更容易解析。另外,通过引入这种一致性(并使用代码棉绒和其他测试),您可以避免goto fail类型错误。

另一个优点是您的代码覆盖率报告将让您知道是否忘记包含format不为null 的测试。对于三元运算符,情况并非如此。


我的首选替代方法-如果您处在“尽快获得回报”的范围内,而不是针对某个方法的多次回报:

private string GetFormattedValue()
{
    if (format != null) {
        return string.Format(format, value);
    }

    return value.ToString();
}

因此,您可以查看最后一个返回值以查看默认值。

不过,保持一致很重要-使您的所有方法都遵循一个或另一个约定。


1
第一个示例似乎是不好的做法,因为value.ToString()当format为非null时会不必要地调用它。在一般情况下,这可能包括非平凡的计算,并且可能比包含格式字符串的版本花费更长的时间。例如,考虑value将PI存储到一百万位小数的a和仅请求前几位数字的格式字符串。
史蒂夫(Steve)

1
为什么不产生private string GetFormattedValue() => string.Format(format ?? "{0}", value); 相同的影响,并使用单元测试来确保正确性,而不是依赖调试器。
Berin Loritsch '17

1
虽然我同意三元可能不太清楚,但空终止符可以使情况清楚。至少在这种情况下。
Berin Loritsch

1
亲爱的日记:今天,我读到了使用著名的(已有40年左右)范例,惯用语和运算符编写清晰简洁的代码的原因,即引号,双引号是巧妙的双引号,不带引号-而是写得过于冗长,违反了代码干燥,不使用上述运营商,成语和范式,而不是设法避免任何可能可能似乎神秘的只有一个五岁之前没有编程背景-是清晰代替。地狱,我一定真的很老,我的日记...我应该有机会学习Go。
vaxquis

1
“将三元运算符用于更复杂的逻辑将是不易理解的”虽然确实如此(并且我已经看到人们使逻辑过于复杂),但对于OP的代码而言并非如此,对于三元运算符也不是特定的事情。我完全有信心地说,这条线太长了。gist.github.com/milleniumbug/cf9b62cac32a07899378feef6c06c776是我重新格式化的方式。
milleniumbug

1

我不认为这种技术可以通过调试来证明。我自己已经遇到过这种方法一千次了,而且我不时地这样做,但是我始终牢记Martin Fowler关于调试的看法:

人们还低估了他们花费的调试时间。他们低估了寻找一个长虫所花费的时间。通过测试,我立刻知道添加错误的时间。这样一来,我便可以立即修复该错误,然后再对其进行隐藏和隐藏。没有比调试更令人沮丧或浪费时间的事情了。如果我们一开始就不创建错误,那会不会快得多呢?


马丁·福勒(Martin Fowler)是个聪明人,我很喜欢阅读他(和您)的观点。尽管我坚信测试是必要的,并且应该花更多的时间进行这项工作,但是我们都是容易犯错误的人这一事实表明,没有任何测试可以消除所有的错误。因此,调试将始终是程序开发和支持过程的一部分。
tale852150

1

我认为有些人正忙于与问题相关的问题,例如三元运算符。是的,很多人都讨厌它,所以无论如何提起它也许是件好事。

关于问题的焦点,将返回的语句移出以供变量引用...

这个问题有两个我不同意的假设:

  1. 第二种变体更清晰或更易读(我说相反的说法是对的),并且

  2. 每个人都使用Visual Studio。我已经使用过Visual Studio多次,并且可以很好地使用它,但是我通常会使用其他东西。我会怀疑一个强制使用特定IDE的开发环境。

将某物分解为一个命名变量很少会使任何内容难以阅读,几乎总是相反。某人执行此操作的特定方式可能会引起问题,例如,如果自我证明文件的霸主确实这样做,var thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...那显然是不好的,但这是一个单独的问题。var formattedText = ...很好

在这种特定情况下,由于我们谈论的是1-线性,因此在很多情况下,变量不会告诉您函数名称尚未告诉您。因此,变量的添加量不大。调试参数仍然可以保留,但是再次,在这种特定情况下,我看不到任何可能成为调试重点的东西,并且以后如果有人以某种方式需要该格式进行调试或其他任何操作,都可以很容易地更改它。

通常,您确实要求通用规则(您的示例只是一个通用形式的示例),所有支持变量1(2-线性)的观点都是正确的。这些是好的指导方针。但是准则必须灵活。例如,我正在处理的项目现在每行最多可包含80个字符,因此我拆分了很多行,但是我通常会发现81-85行的字符会难以拆分或降低可读性,因此我将其留给限制。

由于不可能增加价值,因此对于给出的特定示例,我不会做两行。我会做变体2(1-线性),因为在这种情况下,点的强度不足以做其他事情。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.