7

在 C Sharp 中,如何设置一个 if 语句来检查多个条件之一是否为真?它必须只有一个条件,如果零个或两个或多个为真,则 if 应为假。

4

6 回答 6

9

你可以写一个辅助方法。这样做的好处是短路,只评估必要的数量,

public static bool IsExactlyOneTrue(IEnumerable<Func<bool>> conditions) {
    bool any = false;
    foreach (var condition in conditions) {
        bool result = condition();
        if (any && result) {
            return false;
        }
        any = any | result;
    }
    return any;
}
于 2012-05-24T21:03:22.907 回答
5

您可以使用将布尔值组合成一个bool序列,然后应用 LINQ:

bool[] conditions = new bool[] { cond1, cond2, cond3, cond4 };
bool singleTrue = conditions.Count(cond => cond) == 1;

对于两个布尔值,独占 or 变得更简单:

bool singleTrue = cond1 != cond2;

编辑:为了实现按需评估短路,我们需要将我们的bool序列提升为一个Func<bool>序列(其中每个元素都是一个封装条件评估的函数委托):

IEnumerable<Func<bool>> conditions = // define sequence here
int firstTrue = conditions.IndexOf(cond => cond());
bool singleTrue = firstTrue != -1 && 
                  conditions.Skip(firstTrue + 1).All(cond => !cond());

上面的代码片段假设存在基于谓词的IndexOf运算符,该运算符在当前版本的 LINQ 下不可用,但可以定义为扩展方法,如下所示:

public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    int i = 0;

    foreach (T element in source)
    {
        if (predicate(element))
            return i;

        i++;
    }

    return -1;
}

用于测试的样本数据(可以在每个上设置断点falsetrue在评估之后设置断点):

IEnumerable<Func<bool>> conditions = new Func<bool>[] 
{ 
    () => 
        false,
    () => 
        true,
    () => 
        false,
    () => 
        false,
};
于 2012-05-24T21:02:06.943 回答
5
List<Func<Customer, bool>> criteria = new List<Func<Customer, bool>>();

criteria.Add(c => c.Name.StartsWith("B"));
criteria.Add(c => c.Job == Jobs.Plumber);
criteria.Add(c => c.IsExcellent);

Customer myCustomer = GetCustomer();

int criteriaCount = criteria
  .Where(q => q(myCustomer))
  // .Take(2)  // optimization
  .Count()
if (criteriaCount == 1)
{
}

Jason 的方法签名的 Linq 实现:

public static bool IsExactlyOneTrue(IEnumerable<Func<bool>> conditions)
{
  int passingConditions = conditions
    .Where(x => x())
    // .Take(2) //optimization
    .Count();
  return passingConditions == 1;
}
于 2012-05-24T21:04:48.117 回答
4

为简单起见,您可以保持运行计数:

int totalTrue = 0;
if (A) totalTrue++;
if (B) totalTrue++;
if (C) totalTrue++;
...
return (1 == totalTrue);
于 2012-05-24T20:59:37.360 回答
2

我认为这可以解决问题

 int i= 0;
 if ( (!A || ++i <= 1) && 
      (!B || ++i <= 1) && 
      (!C || ++i <= 1) && 
      ... && 
      (i == 1))

如果我对此没有想错的话,这if将是错误的i > 1。如果i永远不会增加并且我们达到最后一个条件,则将是错误的,因为i == 0

于 2012-05-24T21:04:12.810 回答
0

这些答案中的大多数都将起作用并具有“良好的性能”。但最简单的答案是:

if( (A & !(B || C)) || 
    (B & !(A || C)) ||
    (C & !(A || B)) )
{
   ...
}

你最终会多次评估 A/B/C,所以这只有在你有简单的布尔值时才有用。

于 2012-05-28T17:49:30.393 回答