检查不能彼此共存的按位标志

时间:2013-07-30 12:33:15

标签: c# java

我有旗帜

int A = 1;
int B = 2;
int C = 4;

我想检查一下,只能为一个函数指定一个标志

check(A | B | C) ; // invalid
check(A); // valid
check(B); // valid
check(B | C); // invalid
void check(int flags) {
    // check that if A is specified, then B and C can't
    // check that if B is specified, then A and C can't
    // check that if C is specified, then B and A can't
}

如果没有大量的“if”陈述,我怎样才能做到这一点?

5 个答案:

答案 0 :(得分:6)

要将位设置为 n ,您需要设置值 2 ^ n

因此,如果您想检查是否只指定了其中一个标志,那么您只想询问该数字是否为2的幂。

以下是关于如何执行此操作的问题:How to check if a number is a power of 2

正如格雷厄姆所说,你可以读到这个问题,说必须设置一个比特(即它不能为零)。所以要做到这一点,另外,检查它是否为非零并且它小于或等于C.

答案 1 :(得分:2)

也许不是最优雅的解决方案,但我认为它应该有效:

bool check(int flags) {
    int A = 1;
    int B = 2;
    int C = 4;

    return 
        flags == 0 ||
        flags == A ||
        flags == B ||
        flags == C;
}

答案 2 :(得分:0)

为什么不使用Enum作为标志,并检查枚举中是否定义了int值。

enum Flags
{
    A = 1,
    B = 2,
    C = 4
}

void Check(int flags)
{
    bool isValid = Enum.IsDefined(typeof(Flags), flags);
    ...
}

答案 3 :(得分:0)

这是一个带开关的实现:

void check(int flags) {
  swicth (flags & (A | B | C)) {
    case A:
    case B:
    case C:
    case 0:
      return true;
    default:
      return false;
  }
}

仅当ABC为文字时,才会有效(在C#中),即标有const。否则你也可以这样做:

void check(int flags) {
  int relevantPart = flags & (A | B | C);
  return relevantPart == A || relevantPart == B || relevantPart == C || relevantPart == 0;
}

否则使用二次幂技巧(来自Joe的回答):

void check(int flags) {
  int relevantPart = flags & (A | B | C);
  return (relevantPart & (relevantPart - 1)) == 0;
}

我假设可能存在比三个最小位更多的有效位,并且它们将被忽略。我还假设ABC都没有效(这在我的解释中并不存在)。

答案 4 :(得分:0)

我打算发表评论,但格雷厄姆所说的话非常重要,值得详细阐述。

当您特别希望能够设置倍数时,通常会使用标志。以下是我们的任务枚举

的示例
namespace Shared.Enumerations
{
    [Flags]
    public enum TaskStatusEnum
    {
        NotSet = 0,
        Open = 1,
        Canceled = 2,
        Complete = 4,
        OnHold = 8,
        Inactive = 32,
        All = Open | Canceled | Complete | OnHold | Inactive
    }
} 

我们这样做,所以我们可以说给我们任何开放或暂停的任务。

 TaskList activeTasks = taskListManager.TaskList.FindAll(target.Name, target.TaskType, (TaskStatusEnum.Open | TaskStatusEnum.OnHold));

当然,使用普通枚举,您一次只能设置一件事。您实际上可以执行以下操作。

[TestMethod]
public void checkEnumVals()
{
        var ts = TaskStatusTestEnum.Open;
        ts |= TaskStatusTestEnum.OnHold;

        bool matchBoth = false;
        if ((ts & TaskStatusTestEnum.OnHold) == TaskStatusTestEnum.OnHold && (ts & TaskStatusTestEnum.Open) == TaskStatusTestEnum.Open)
           matchBoth = true;

        Assert.IsTrue(matchBoth);
}

我不会建议这样的事情。