仅使用'if-else'语句的'else'部分是否可以接受?

时间:2009-09-24 18:32:11

标签: c# conditional if-statement

有时,我觉得检查所有条件是否都是真的更容易,但是只处理“其他”情况。

我想我有时会觉得更容易知道某些内容是有效的,并假设所有其他情况都无效。

例如,假设我们只关心何时出错:

object value = GetValueFromSomeAPIOrOtherMethod();

if((value != null) && (!string.IsNullOrEmpty(value.Prop)) && (possibleValues.Contains(value.prop)))
{
    // All the conditions passed, but we don't actually do anything
}
else
{
    // Do my stuff here, like error handling
}

或者我应该改为:

object value = GetValueFromSomeAPIOrOtherMethod();

if((value == null) || (string.IsNullOrEmpty(value.Prop)) || (!possibleValues.Contains(value.prop)))
{
    // Do my stuff here, like error handling
}

或(我觉得很难看):

object value = GetValueFromSomeAPIOrOtherMethod();

if(!((value != null) && (!string.IsNullOrEmpty(value.Prop)) && (possibleValues.Contains(value.prop))))
{
    // Do my stuff here, like error handling
}

23 个答案:

答案 0 :(得分:60)

虽然对我来说很少见,但我有时觉得以这种形式写作会在某些情况下产生最清晰的代码。寻找提供最清晰的表格。编译器不关心,并且应该基本上(可能确切地)生成相同的代码。

但是,可能更清楚的是,定义一个在if()语句中赋值的布尔变量,然后将代码写为该变量的否定:

bool myCondition = (....);
if (!myCondition)
{
    ...
}

答案 1 :(得分:55)

在else中使用带有语句的空if块是......只是糟糕的样式。对不起,这是我的一个小小的烦恼。它没有任何功能上的错误,只是让我的眼睛流血。

简单!输出if语句并将代码放在那里。恕我直言,它可以降低噪音并使代码更具可读性。

答案 2 :(得分:28)

我应该先说这是我个人的偏好,但我发现自己通常会将验证逻辑从代码中拉出来并进入自己的验证函数。那时,你的代码变得更加“整洁”,只需说:

if(!ValidateAPIValue(value))

在我看来,这看起来更简洁易懂。

答案 3 :(得分:9)

仅使用else部分是不可接受的。你不需要去应用De-Morgan的规则,而不是整个表达。也就是说,从if (cond)转到if (!(cond))

答案 4 :(得分:9)

我认为这是完全不可接受的。

唯一的理由是避免在表达式周围出现一个否定和一对括号。我同意你的例子中的表达是可怕的,但它们开始时是不可接受的错综复杂的!将表达式划分为可接受的清晰度部分,将它们存储到布尔值中(或者用它们制作方法),然后将它们组合起来制作if语句条件。

我经常使用的一个类似设计是提前退出。我不写这样的代码:

if (validityCheck1)
{
    if (validityCheck2)
    {
        // Do lots and lots of things
    }
    else
    {
        // Throw some exception, return something, or do some other simple cleanup/logic (version 2)
    }
}
else
{
    // Throw some exception, return something, or do some other simple cleanup/logic. (version 1)
}

相反,我写这个:

if (!validityCheck1)
{
    // Throw some exception, return false, or do some other simple logic. (version 1)
}

if (!validityCheck2)
{
    // Throw some exception, return false, or do some other simple logic. (version 2)
}

// Do lots and lots of things

这有两个好处:

  1. 只有少数输入案例无效,并且处理起来很简单。它们应该立即处理,以便我们尽快将它们从我们的心理模型中删除,并完全专注于重要的逻辑。特别是当嵌套的if语句中存在多个有效性检查时。

  2. 处理有效案例的代码块通常是方法的最大部分,并包含自己的嵌套块。如果这个代码块本身不在if语句中嵌套(可能多次),那就不那么混乱了。

  3. 因此代码更具可读性,更容易推理。

答案 5 :(得分:3)

提取您的条件,然后致电

if(!ConditionsMetFor(value))
{
   //Do Something
}

答案 6 :(得分:3)

虽然这并不总是实用,但我通常更愿意改变

if (complexcondition){} else {/*stuff*/}

if (complexcondition) continue;
/*stuff*/

(或与returnbreak等分开。)当然,如果条件复杂,你可以用几个条件替换它,所有条件都会导致代码突破它正在做的事情。这主要适用于验证和错误检查类型的代码,如果出现问题,您可能希望退出。

答案 7 :(得分:2)

这是不好的风格,考虑一些非常有用的选择:

使用保护条款

object value = GetValueFromSomeAPIOrOtherMethod();

if((value != null) && (!string.IsNullOrEmpty(value.Prop)) && (possibleValues.Contains(value.prop)))
{
    return;
}
// do stuff here

将条件提取到自己的方法中,这使事情保持逻辑且易于阅读:

bool ValueHasProperty(object value)
{
    return (value != null) && (!string.IsNullOrEmpty(value.Prop)) && (possibleValues.Contains(value.prop));
}

void SomeMethod()
{
    object value = GetValueFromSomeAPIOrOtherMethod();
    if(!ValueHasProperty(value))
    {
        // do stuff here
    }
}

答案 8 :(得分:2)

您的问题与favorite programmer ignorance pet peeve's

上的答案(简化条件)类似

对于不支持until结构的语言,链接多个NOT 会让我们的眼睛流血

哪一个更容易阅读?

此:

while (keypress != escape_key && keypress != alt_f4_key && keypress != ctrl_w_key)

或者这个:

until (keypress  == escape_key || keypress == alt_f4_key || keypress == ctrl_w_key)

我认为后者比第一个更容易理解。第一个涉及太多的NOT和AND条件使逻辑更强粘性,它会强制您在确定您的代码确实正确之前读取整个表达式,并且它将更多如果你的逻辑涉及复杂的逻辑,那就更难阅读了(需要链接更多的AND,非常粘稠)。

大学期间,德摩根定理在我们班上讲授。我真的很感激使用他的定理可以简化逻辑。因此对于直到语句不支持的语言构造,请使用:

while !(keypress  == escape_key || keypress == alt_f4_key || keypress == ctrl_w_key)

但由于C不支持无括号while / if语句,我们需要在DeMorgan代码上添加括号:

while (!(keypress  == escape_key || keypress == alt_f4_key || keypress == ctrl_w_key))

这可能促使Dan C评论说DeMorgan的代码更多地伤害了他对favorite programmer ignorance pet peeve's

的答案的看法

但实际上,DeMorgan代码比多个NOTS和粘性AND

更容易阅读

<强> [编辑]

您的代码(DeMorgan的代码):

object value = GetValueFromSomeAPIOrOtherMethod();

if ( value == null || string.IsNullOrEmpty(value.Prop) 
   || !possibleValues.Contains(value.prop) )
{
    // Do my stuff here, like error handling
}

..完全没问题。事实上,大多数程序员(尤其是那些没有try / catch / finally结构的语言)确实满足条件(例如,不使用空指针,具有适当的值等)在继续操作之前。

注意:我冒昧地删除了代码中多余的括号,也许你来自Delphi / Pascal语言。

答案 9 :(得分:2)

如果我看到“如果”,我希望它能做点什么。

if(!condition)

更具可读性。

if(condition) {
  //do nothing
}
else {
  //do stuff
}

基本上写着,“如果我的条件得到满足,就什么都不做,否则就做点什么。”

如果我们要将您的代码读作散文(这种良好的,自我记录的代码应该能够以这种方式阅读)那太简单了,并且介绍了比完成目标。坚持使用“!”。

答案 10 :(得分:1)

当我的大脑能够轻松地将自己包含在成功的逻辑中时,我就会这样做,但理解失败的逻辑是很麻烦的。

我通常只是发表评论“// no op”,所以人们都知道这不是一个错误。

答案 11 :(得分:1)

我的回答通常是不......但我认为好的编程风格是基于一致性的......

所以,如果我有很多看起来像

的表达式
if (condition)
{
    // do something
}
else
{
    // do something else
}

然后偶尔出现“空”if块很好,例如

if (condition)
{ } // do nothing
else
{
    // do something else
}

原因在于,如果您的眼睛多次看到某些东西,他们就不太可能注意到变化,例如:一丁点 ”!”。因此即使孤立地做一件坏事,它很可能会让某人在未来维护代码时意识到这个特殊的if..else ......与其他...不同...

可能接受的其他特定场景是某种状态机逻辑,例如

if (!step1done)
{}  // do nothing, but we might decide to put something in here later
else if (!step2done)
{
    // do stuff here       
}
else if (!step3done)
{
    // do stuff here       
}

这显然强调了状态的顺序流动,每个阶段执行的步骤(即使它什么都没有)。我更喜欢它... ...

if (step1done && !step2Done)
{
    // do stuff here       
}
if (step1done && step2done && !state3Done)
{
    // do stuff here       
}

答案 12 :(得分:1)

我偶尔会发现自己处于相关但略有不同的情况:

if ( TheMainThingIsNormal () )
    ; // nothing special to do

else if ( SomethingElseIsSpecial () )   // only possible/meaningful if ! TheMainThingIsNormal ()
    DoSomethingSpecial ();

else if ( TheOtherThingIsSpecial () ) 
    DoSomethingElseSpecial ();

else // ... you see where I'm going here

// and then finish up

取出空块的唯一方法是创建更多嵌套:

if ( ! TheMainThingIsNormal () )
{
    if ( SomethingElseIsSpecial () )
        DoSomethingSpecial ();

    else if ( TheOtherThingIsSpecial () ) 
        DoSomethingElseSpecial ();

    else // ...
}

我没有检查异常或验证条件 - 我只是在处理特殊或一次性案件 - 所以我不能提前纾困。

答案 13 :(得分:1)

我发现它是不可接受的(即使我确定我过去已经完成了),因为它有一个空块。这意味着应该做点什么。

我看到其他问题表明第二种方式更具可读性。就个人而言,我说你的例子都不是特别易读。您提供的示例是请求“IsValueValid(...)”方法。

答案 14 :(得分:1)

这不是一个好习惯。如果你使用ruby,你会这样做:

unless condition
  do something
end

如果您的语言不允许,请执行

if(a){}else{something}

DO

if(!a){something}

答案 15 :(得分:0)

为了便于阅读,我总是尝试将这样的大条件重构为属性或方法。所以这个:

object value = GetValueFromSomeAPIOrOtherMethod();

if((value == null) || (string.IsNullOrEmpty(value.Prop)) || (!possibleValues.Contains(value.prop)))
{
    // Do my stuff here, like error handling
}

变成这样:

object value = GetValueFromSomeAPIOrOtherMethod();

if (IsValueUnacceptable(value))
{
    // Do my stuff here, like error handling
}

...

/// <summary>
/// Determines if the value is acceptable.
/// </summary>
/// <param name="value">The value to criticize.</param>
private bool IsValueUnacceptable(object value)
{
    return (value == null) || (string.IsNullOrEmpty(value.Prop)) || (!possibleValues.Contains(value.prop))
}

现在,您可以随时重用方法/属性,并且您不必在使用方法中考虑太多。

当然,IsValueUnacceptable可能是一个更具体的名称。

答案 16 :(得分:0)

第一

object value = GetValueFromSomeAPIOrOtherMethod();
var isValidValue = (value != null) && (!string.IsNullOrEmpty(value.Prop)) && (possibleValues.Contains(value.prop));
if(!isValidValue)
{
    // Do my stuff here, like error handling
}

2cnd:

object value = GetValueFromSomeAPIOrOtherMethod();
if(!isValidAPIValue(value))
{
    // Do my stuff here, like error handling
}

答案 17 :(得分:0)

所有的表情都是一样的吗?在支持短路的语言中,在ands和ors之间进行更改可能是致命的。记住&amp;&amp;&amp;&amp;&amp;&amp; and amp;&amp; and amp;&amp; and amp;&amp; and amp;&amp; and amp; and

转换时要小心。犯了更多错误。

答案 18 :(得分:0)

在这些情况下,您可能希望将验证逻辑抽象到类本身中,以帮助解决应用程序代码的混乱问题。

例如

class MyClass
{
   public string Prop{ get; set; }
   // ... snip ...
   public bool IsValid
   {
      bool valid = false;
      if((value != null) && 
      (!string.IsNullOrEmpty(value.Prop)) && 
      (possibleValues.Contains(value.prop)))
      {
         valid = true
      }
      return valid;
   }
   // ...snip...
}

现在您的应用程序代码

MyClass = value = GetValueFromSomewhere();

if( value.IsValie == false )
{
   // Handle bad case here...
}

答案 19 :(得分:0)

我是DeMorgan规则的粉丝,它带你的ex3并产生你的ex2。空if块是一个心理块imo。你必须停下来阅读存在的任何东西 - 然后你不得不想知道为什么。

如果你必须留下像//这样的评论。然后代码不是很明显。

答案 20 :(得分:0)

将一个块空出if-else后面的样式视为坏样式.. 对于良好的编程习惯,如果你不必写入if if block,你需要把(!)'Not'in if block ..不需要写别的

如果(条件) //空白 其他 //代码

可以替换为

如果(!条件) //代码

这也节省了额外的代码行。

答案 21 :(得分:0)

我喜欢第二个版本。它使代码更干净。实际上,这是我在代码审查期间要求纠正的事情之一。

答案 22 :(得分:0)

我不会在C#中这样做。但是我在Python中这样做,因为Python有一个关键字意味着“什么都不做”:

if primary_condition:
   pass
elif secondary_condition1:
   do_one_thing()
elif secondary_condition2:
   do_another_thing()

您可以说{ }在功能上等同于pass。但它(不是人类)在语义上是等价的。 pass表示“什么都不做”,而对我来说,{ }通常意味着“此处曾经有过代码,现在却没有。”

但总的来说,如果我达到甚至是一个问题,是否在条件面前坚持!使得阅读更难,我就遇到了问题。如果我发现自己编写这样的代码:

while (keypress != escape_key && keypress != alt_f4_key && keypress != ctrl_w_key)

我很清楚,从长远来看,我实际上想要的更像是这样:

var activeKeys = new[] { escape_key, alt_f4_key, ctrl_w_key };
while (!activeKeys.Contains(keypress))

因为这明确了一个概念(“这些键处于活动状态”),它只隐含在前面的代码中,并使逻辑“这就是你按下非活动键时发生的事情”,而不是“这就是当一个键不是一个ESC,ALT + F4或CTRL + W被按下。“