使用ELSE编程不好吗?[关闭]


18

我经常遇到由于使用该ELSE构造而导致的错误。一个典型的例子是:

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

对我来说,这是安全问题。我知道passwordCheck可能是布尔值,但是我不会将应用程序的安全性放在上面。如果它是字符串,int等会怎样?

我通常会尝试避免使用ELSE,而是选择两个完全独立的IF语句来测试我的期望。然后,其他任何东西都将被忽略,或者将被专门处理。

当然,这是防止错误/安全问题进入您的应用程序的更好方法。

你们是如何做到的?


3
您的安全问题是什么?“ passwordcheck”是什么意思?有密码检查吗?需要进行密码检查吗?用户已通过?用户未能输入正确的密码?
LennyProgrammers 2010年

20
I know that passwordCheck is likely to be a boolean...你什么意思?任何强类型的语言。passwordCheck将成为想要的任何东西。
Bobby 2010年

31
我认为不好的缩进做法比使用else语句会导致更多的错误……
gablin 2010年

15
这似乎很奇怪。首先,您抱怨passwordCheck()可能不是布尔值的可能的返回类型(这可能是一个合理的问题),然后将其归咎于此else?我看不出是什么else原因造成的。
David Thornley 2010年

8
嗯,我认为问其他使用是否不好的编程是不好的编程
Muad'Dib 2010年

Answers:


90

else块应始终包含您想要默认行为的内容。

无需避免使用它们,只需小心使用它们即可。

在您的示例中,默认状态应为不允许访问。进行一些重构可以使您:

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

例如,如果密码检查有效,则让他们进入,否则显示一些错误消息始终有效。

当然,您可以使用else if而不是完全分开的if语句向逻辑添加其他检查。


2
在该示例的上下文中,@ dave.b我想这将是一个安全问题,但是如果这在您要查看的代码库中到处都是,那么这更多地表明了编写该代码的人需要更多一点练习:)
RYFN 2010年

9
有人可以详细说明这一点吗?if(!something){做} else {do b}与if(something){do b} else {do a}在逻辑上等效吗?我试图了解在安全性方面有什么区别?
克里斯,2010年

11
@Chris:我认为OP使用的是弱类型语言。因此passwordCheck,fe可以是任何东西,因为内部错误null,它将呈现passwordCheck == falsefalse用户并且允许用户登录。
鲍比2010年

1
@Chris,我的理解是默认状态是允许访问,这不一定是明智的。
RYFN 2010年

3
是的,这很大程度上取决于语言。在C#中,当if需要bool,变量必须被明确指定,所有路径必须返回一个值,等等,我想不出任何理由的顺序ifelse除了可读性会事宜。也就是说,if(a){b();}{c();}应等于if(!a){c();{b();}。在JavaScript中,在另一方面,你必须要知道,passwordCheck可能是undefined
蒂姆·古德曼

57

不,没有问题ELSEELSE不是新的GOTO。实际上,使用2 IF代替ELSE会导致多个问题。

范例一:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

您看到复制粘贴了吗?它只是在等待您改变一个而忘记另一个的那一天。

示例二:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

如您所见,IF因为条件已经更改,所以第二项也将对第一项执行。在实际程序中,很难发现此类错误。


12
我同意这一点。复制-粘贴的if声明和反相其布尔表达式只是为了避免else-现在是不好的编程!你不仅是重复码(编程),你也降低系统性能(如果检查真的很复杂,你现在做了两次- ba--嗯,嗯,你知道他们怎么说过早的优化是什么如今... 编程不太好!)。
gablin

说实话,如果check应该使用自己的方法返回true / false
billy.bob 2010年

很好的例子,不要忘记使用2 if check和一个if else else的性能。我想在大多数语言中,使用if / else会比使用两个if语句和一个条件为not的情况更好。
克里斯,2010年

1
dave.b:可以,但是它可以从简单开始并缓慢增长;在我的示例中,进行复杂的检查可以使问题更加明显。
user281377 2010年

3
是的-别忘了条件在大多数语言中都会产生副作用,如果条件中确实存在某些功能,您实际上无法通过外观看出来。
David Thornley 2010年

18

总有一个ELSE。如果你写

if(foo)
  bar();

你实际上写

if(foo)
{
  bar();
}
else
{
   // do nothing
}

无论您采用哪种ELSE路径,都是您的责任。


7
-1。这个答案太荒谬了。我并不是说这不是真的。它只是错过了重点。编译从您的代码生成的内容与您编写的编码实践和错误无关

3
问题是“正在使用ELSE不良编程吗?”。我的回答是:您可以假装它不存在,但不能避免。
LennyProgrammers

5
什么语言 ?在Java中else不在字节码中:P
IAdapter 2011年

@ acidzombie24-考虑任何问题的“其他”是非常好的做法。有时,它只是-不要everyhtign否则在温控功能,但它确实表明你有想过这个问题
马丁贝克特

14

我个人倾向于尽量避免else,但这不是出于任何安全问题。

读取代码时,嵌套语句使遵循逻辑变得更加困难,因为您需要记住哪一组条件将导致该逻辑。因此,我非常喜欢提前退出:

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

这也适用于我将在其中使用的forand while循环continuebreak只要它避免一定程度的缩进。

克里斯·拉特纳(Chris Lattner)表示,它比我在LLVM编码标准中做的更好。


我同意。“其他”会创造出心理上的“叉子”,而我们人类是有序的生物。
user187291 2010年

Fyi,这称为警卫状态
CaffGeek 2010年

@Chad:啊,谢谢,总是很高兴为事情起名字:)
Matthieu M.

1
+1。这是我所能接受的唯一得分> 1的答案。*writes an answer*

我不会尝试避免这样的事情,但是我想我同意你写的内容。关键是要测试的东西应该是例外,而不是正常情况(stackoverflow.com/questions/114342/…)。此处,身份验证失败是例外,并且(仅)应进行测试。
hlovdal

5

然后只需更换它们,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}

21
怎么If ((passwordCheck == true) == true)样 :-)
河马

1
好吧,这是我的风格,以提高可读性。您可以编写if(!value),但我更喜欢if(value!= true)
AhmetKakıcı2010年

7
它不会提高可读性。它只会增加噪音。更糟糕的是,因为害怕布尔运算符而编写嵌套if构造的程序员。
ak2 2010年

3
如果变量名是描述性的,则没有理由:如果需要(someBooleanValue == true)。这样会更好:if(validPassword){...
Mark Freedman 2010年

好吧,我只是替换了问题中的代码块。如果您阅读了我的第一条评论,我说我更喜欢使用if(value!= true)而不是if(!value)。我希望您能看到if(value)和if(!value)之间的区别。我的意思是我不使用补码运算符。否则,您是对的,true表示true。
AhmetKakıcı10年

3

像Matthieu M.一样,我更喜欢提早退出而不是深度嵌套的else块...它说明了防御性很好的编程(如果条件不好,就没有继续的意义)。很多人会不同意我们,而是选择一个独特的出口点。(我认为)这不是辩论的重点。

现在,我当然会else在有意义的时候使用它,特别是对于简单,简短的替代方案。如前所述,重复测试是浪费时间(程序员和CPU),混乱的源头,也是后来的错误(当一个被更改时,而不是另一个)。
有时,我会在else零件上添加一条注释,以提醒当时的情况(尤其是if零件较长,例如,在旧代码中)。

请注意,函数式编程的一些极端拥护者建议完全摆脱if模式支持,以支持模式匹配。:-)


1
附带说明:如果您使用纯函数式语言编写了if构造,那么您确实需要使用else。每个表达式都必须返回一些东西!
tokland 2010年

2

使用ELSE没错。但是,这可能导致难以阅读和理解的代码过于复杂。这可能表明设计不好。它肯定表示需要测试的其他用例。

如果可以的话,请尝试删除ELSE-但不要对此感到偏执。Steve McConnell在“代码完成”中将此直线代码称为“直线代码”。也就是说,您的代码中有一条简单清晰的路径。

尝试解决您的特定问题的方法:

  • 使用多态。在您系统的边界上,验证用户的安全凭证。如果它们是合法的,则返回一个会话对象-可以访问系统的相关部分或引发异常。但是,这可能会使您的系统更加复杂。因此,您可以决定更容易理解和维护的内容。

通常,以下内容可能有助于减少代码中的ELSE:

  • 好的要求可以减少代码中对此类决策的需求。您可能根本不需要实现用例(其他)。
  • 更清晰的设计。最大的内聚力,最小化耦合。这样可以确保组件不会重复其他组件中做出的决策。
  • 异常处理以管理错误案例。
  • 多态性(请参见上面的示例)。
  • switch语句-这些是荣耀的ELSE,但在某些情况下会更好。

2
我还将包括“将复杂的布尔检查移动到单独的函数中”。
gablin

2

根据所使用的语言,您对代码是否存在安全漏洞的假设可能是正确的,也可能不是。在C代码中,这可能是个问题(特别是因为在C中,布尔值只是一个非零或零的整数),但是在大多数强类型语言(即运行时类型检查)中,如果将passwordCheck变量声明为布尔值,没有办法给它分配其他东西。实际上,if无论您使用布尔运算符还是仅使用值,谓词中的所有内容都必须解析为布尔值。如果设法将另一种类型的对象绑定到passwordCheck运行时,则会抛出某种非法的强制转换异常。

简单的if / else构造比if / if构造更易于阅读-如果有人尝试翻转该构造,则不太容易出现疏忽的问题。让我们再举一个相同的例子:

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

您上面要执行的互斥子句的含义已丢失。这就是if / else构造所传达的含义。两个相互排斥的执行分支,其中一个始终运行。这是安全性的重要组成部分-确保letThemIn致电后没有办法denyAccess

为了代码清晰起见,并为了确保关键部分得到最大程度的保护,它们应位于主要子句( if部分)内。默认的不符合行为应在替代子句(该else部分)中。例如:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

注意:在使用不同的语言时,我已经开发了一个编码习惯,它确实有助于避免“如果是字符串该怎么办?”的问题。本质上,它是将常量放在布尔表达式中。例如,而不是检查passwordCheck == false检查false == passwordCheck。这也避免了C ++中可能发生的意外赋值问题。使用这种方法,如果我键入=而不是,编译器将抱怨==。在Java和C#等语言中,编译器会将if子句中的赋值视为错误,但是C ++会很乐意接受它。这就是为什么我也倾向于对null第一个进行空检查。

如果您定期更改语言,则将常量放在第一位非常有帮助。但是,在我的团队中,它与编码标准相反,并且编译器仍然会抓住这些问题。要打破它可能很难。


1

else在编程时不好用就像otherwise在讲话时不好用。

当然,它们都可能以不好的方式使用,但这并不意味着要避免它们仅仅是因为您犯了一个错误,而恰巧包括了它们。如果许多错误取决于语句中缺少的default情况,我不会感到惊讶switch


1

可以将Else您的应用程序流程列入白名单。您检查是否应该允许应用程序流继续运行的条件,如果不满足这些条件,则将Else执行您以解决问题,中止应用程序执行或执行类似操作。

Else 本身并不坏,但是如果使用不当,则会看到不良的效果。

另外,关于您的陈述

“我知道passwordCheck可能是一个布尔值,但是我不会将其应用程序安全性放在上面。”

对于您开发的方法,总是返回一种数据类型。尽管PHP Core充斥着返回两个或多个数据类型的代码,但这是一种不好的做法,因为它会使函数调用变得guess测。如果您必须返回多个数据类型,请考虑引发异常(我发现这通常是我想返回另一种数据类型的原因-发生了可怕的错误),或者考虑重新构造代码以便您可以仅返回一种数据类型。


我不知道有些语言返回的数据类型不只一种!您是在指多态函数吗?我相信,每当我重载一个函数时,它总是返回相同的数据类型,尽管它可能采用不同的参数。
Michael K 2010年

1
在松散类型的语言中,尤其是在PHP中,函数可以返回多个数据类型。例如:stristr-“返回匹配的子字符串。如果找不到针,则返回FALSE”
Craige 10'Dec

@ Michael,PHP函数可以返回您想要的任何内容。数据类型没有限制。我最复杂的函数返回true / false / null,但是没有什么(常识除外)阻止您编写返回true / integer / null / string / float / array的函数。
TRiG 2010年

有趣。我从未使用过PHP。不过,感谢您的解释-将来可能会对我有所帮助!那么像Javascript呢?
Michael K 2010年

@Michael-确实类似于Javascript。
Craige 2010年

1

首先。大声笑!根本没有避免其他原因的理由。以任何方式,形状或形式都不错。

如果有的话,代码应该是

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

那里没有两个if,也没有别的。这是我在所有应用程序中所做的事情,除了我抛出异常的应用程序。我的函数中捕获了异常,该函数检查url以显示正确的页面(或者可以将catch / check放入asp.net错误函数中)。它会打印出一个通用页面,该页面显示未授权或我在异常中使用的任何消息(我总是检查异常的类型并设置http状态代码)。

-编辑-如ammoQ示例中所示,两个if是荒谬的。真的,其他情况与if一样好或更好。如果有什么需要避免的话(尽管我个人没有。但是我确实使用return并破坏了很多),因为据说更多的代码路径会增加发生错误的可能性。见环复杂性

-编辑2-如果您担心是否使用其他方法。我还要注意,我的偏好是将最短的代码块放在顶部,例如

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

而不是

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}

0

我希望在条件允许之前设置默认值。我觉得它更容易阅读,更明确,但这只是一个偏爱。我倾向于尝试避免代码中出现负面条件。我不是检查!foo或false == foo的忠实拥护者,我觉得else是有条件的负数。

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

代替 ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

上一个代码块对我来说似乎更容易阅读。对我来说,对我的代码有某种怀疑的偏执似乎是自然的。无论任何条件设置默认值,我都会感觉很舒服:P


0

我认为应该尽可能避免使用任何分支逻辑。尽管ELSE或IF没什么问题,但是有很多方法可以编写代码以最小化使用任何分支逻辑的需求。我并不是说可以完全消除分支逻辑-在某些地方将需要它-但可以重构代码以消除其中的很大一部分。在大多数情况下,这将提高代码的清晰度和准确性。

例如,三元运算符通常也是不错的选择:

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

使用三元方法:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

三元运算符很好地将分支向右移动。

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.