编写此布尔表达式时哪种样式更可取?

时间:2012-10-08 20:45:24

标签: c# coding-style boolean-logic code-readability

我知道这个问题在某种程度上是一种品味问题。我承认这不是我不理解的东西,只是我想听别人的意见。

我需要编写一个带有两个参数的方法,一个布尔值和一个字符串。布尔值在某种意义上(很快就会很明显)是冗余的,但它是该方法必须在两个参数中都采用的规范的一部分,并且如果布尔值具有“错误”值,则必须使用特定的消息文本引发异常。当且仅当字符串不为空或为空时,bool必须为true

所以这里有一些不同的风格(希望!)同样的东西。你找到哪一个是可读,并且符合良好的编码习惯?

// option A: Use two if, repeat throw statement and duplication of message string
public void SomeMethod(bool useName, string name)
{
  if (useName && string.IsNullOrEmpty(name))
    throw new SomeException("...");
  if (!useName && !string.IsNullOrEmpty(name))
    throw new SomeException("...");

  // rest of method
}


// option B: Long expression but using only && and ||
public void SomeMethod(bool useName, string name)
{
  if (useName && string.IsNullOrEmpty(name) || !useName && !string.IsNullOrEmpty(name))
    throw new SomeException("...");

  // rest of method
}


// option C: With == operator between booleans
public void SomeMethod(bool useName, string name)
{
  if (useName == string.IsNullOrEmpty(name))
    throw new SomeException("...");

  // rest of method
}


// option D1: With XOR operator
public void SomeMethod(bool useName, string name)
{
  if (!(useName ^ string.IsNullOrEmpty(name)))
    throw new SomeException("...");

  // rest of method
}


// option D2: With XOR operator
public void SomeMethod(bool useName, string name)
{
  if (useName ^ !string.IsNullOrEmpty(name))
    throw new SomeException("...");

  // rest of method
}

当然,欢迎您提出其他可能性。消息文本"..."类似于“如果'useName'为true,则必须给出名称,如果'useName'为false,则不允许使用名称。”

2 个答案:

答案 0 :(得分:1)

如果方法需要知道字符串是空还是空,它可以自己检查,它不需要强制调用者进行检查,或者冒着调用者(有意或无意)传入的风险错误的布尔值。首先不需要传递这样的布尔值,因此不需要检查它,因为你知道你不会做错。

话虽如此,我不知道为什么有人不会选择选项C,如果他们需要比较两个布尔表达式。 A和B只是多余的。它是为了添加代码而添加代码。这就像在那里投掷无操作。 D只是不必要的深奥。当有一个运算符完全你想要的东西时,为什么依赖于一个独占的或恰好是你想要的(即按位NOT)的事实(这是相等的){{{ 1}})。

答案 1 :(得分:1)

尽管在某些情况下可以使用这些技巧来提高可读性,但在这种情况下,我会说C,D,E是不好的选择 - 它们采用两种不同的预期方案并将它们组合成一个条件,这可能会模糊你的意图/混淆读者。这使得代码更难以维护和调试(您必须仔细考虑一下,了解发生了什么,这会增加您犯错误的风险)。

(我会进一步讨论D和E,并说它们也很糟糕,因为它们将二进制算术应用于布尔值,这意味着您依赖于从bool(true / false)到某个整数值的隐式转换可以安全地进行异或,然后隐式转换回布尔值。我更喜欢避免隐式转换,并小心将bool视为bools)

A,B是更好的选择,因为它们只是简单明确地说明它们对每个预期情景的意义。将来维护代码的人不太可能会对代码在不同条件下的行为表现感到困惑。

但是,这里有一些其他选项可以使代码更容易阅读:

bool nameIsValid = (!string.IsNullOrEmpty(name));
if (useName)
{
  if (!nameIsValid) 
    throw new SomeException("..."); 
}
else if (nameIsValid) 
{
  throw new SomeException("..."); 
}

(使用else保存读者必须重新评估“useName”条件,这可以加快阅读时间)

或者,以更紧凑的方式:

bool nameIsValid = (!string.IsNullOrEmpty(name));
if ((useName && !nameIsValid) || (!useName && nameIsValid))
    throw new SomeException("..."); 

(使用临时变量,您可以压缩/缩小代码并使其更具可读性,同时仍然明确指定您希望涵盖的两个独立方案)

甚至:

bool nameIsValid = (!string.IsNullOrEmpty(name));

// If we are using a name it MUST be valid
if (useName && !nameIsValid)
    throw new SomeException("..."); 

// If we are NOT using a name, it is illegal to supply one
if (!useName && nameIsValid)
    throw new SomeException("..."); 

注释可用于阐明检查的内容,并允许读取代码的人在不考虑条件表达式含义的情况下计算出代码的内容。 (我的意思是很容易看到它的作用,但条件!usename && nameIsValid没有解释为什么我们想要检查它。

最后一个例子是我个人的偏好 - 因为我已经说明了我打算发生的事情以及为什么我打算这样做以及实际编写代码来实现它。这允许任何阅读代码的程序员轻松检查我说的意思。它为您提供了简单的交叉检查。 (有些人会声称这样做的风险是有人可以在不更新评论的情况下修改代码,这会使评论产生误导 - 但如果快速浏览读数显示代码和评论似乎彼此不匹配,那么你知道有些东西是错的,并且值得花时间在那段代码上来修复它。如果它们匹配,快速交叉检查是成功的,你可以更自信的是代码是可能是预期的(所以你只需要考虑设计是否正确,而不是实现)。