【问题标题】:Bitwise flags and Switch statement?按位标志和 Switch 语句?
【发布时间】:2010-10-02 18:19:49
【问题描述】:

我有以下代码(示例),我真的对这么多“if”检查感到不舒服:

public enum Flags 
{
    p1 = 0x01,  // 0001
    p2 = 0x02,  // 0010  
    p3 = 0x04,  // 0100
    p4 = 0x08   // 1000
};      

public static void MyMethod (Flags flag)
{
    if ((flag & Flags.p1) == Flags.p1)
        DoSomething();

    if ((flag & Flags.p2) == Flags.p2)
        DosomethingElse();

    if ((flag & Flags.p3) == Flags.p3)
        DosomethingElseAgain();

    if ((flag & Flags.p4) == Flags.p4)
        DosomethingElseAgainAndAgain();
}

MyMethod(Flags.p1 | Flags.p3);

有什么方法可以让我使用“switch”语句。也许如果我将它们转换为字符串,或者使用数组?

【问题讨论】:

  • switch 语句不是你想要的,因为它相当于一大堆if ... else if ... else if ... 语句,这显然不是你所拥有的。
  • 请注意,在 C# 4.0 中,您可以使用 flag.HasFlag(Flags.p1) 而不是 flag & Flags.p1 == Flags.p1
  • 我认为一系列的 if 检查很清楚,因为它准确地描述了你在做什么。通过将每个部分的逻辑分离到自己的方法中,您已经消除了会掩盖顶级模式(即一系列条件操作)的噪音。
  • Dan Bryant:在大多数情况下你是对的,但是很难看到它并知道所有的标志都被表示了,而且很难确保维护程序员不会进来并且开始尝试通过在其中插入 else 来“优化”它。
  • 这段代码唯一的“错误”是没有漂亮的长注释。

标签: c# enums switch-statement bit-manipulation


【解决方案1】:

这样的?

public static void MyMethod(Flags flag)
{
    // Define action-lookup
    var actionsByFlag = new Dictionary<Flags, Action>
    {
        { Flags.p1, DoSomething},
        { Flags.p2, DosomethingElse},
        { Flags.p3, DosomethingElseAgain},
        { Flags.p4, DosomethingElseAgainAndAgain},
    };

    // Find applicable actions
    var actions = actionsByFlag.Where(kvp => (flag & kvp.Key) == kvp.Key)
                               .Select(kvp => kvp.Value);

    //Execute applicable actions
    foreach (var action in actions)
       action();
}

编辑:如果操作顺序很重要,则可能需要 OrderBy 子句。

【讨论】:

  • 在 C# 4.0 中你可以使用dict.Where(kv =&gt; flag.HasFlag(kv.Key))
  • 我喜欢 @Ani、Dictionary 和 LINQ。感谢@Gabe 和@Callum Rogers 提到新的C# 4.0 功能。
  • 好答案,但定义动作查找在方法之外
  • Caspar:动作查找可能需要仅在该方法内部可用的状态。但是,如果不是,它应该在方法之外定义。
  • 这很好,但老实说,我认为这比简单的一系列测试和方法更难阅读。此版本要求维护开发人员了解许多中级概念,并且(不太关心)执行需要相当长的时间。只要将每个标志案例的逻辑委托给辅助方法,我认为初始实现实际上是相当干净的。
【解决方案2】:

这是Ani 的答案的变体:

public static void MyMethod(Flags flag) 
{ 
    // Define action-lookup 
    var dict = new Dictionary<Flags, Action> 
    { 
        { Flags.p1, DoSomething}, 
        { Flags.p2, DosomethingElse}, 
        { Flags.p3, DosomethingElseAgain}, 
        { Flags.p4, DosomethingElseAgainAndAgain}, 
    }; 

    // Find applicable actions 
    var actions = from value in Enum.GetValues(typeof(Flags))
                  where flag.HasFlag(value)
                  select dict[value];

    //Execute applicable actions 
    foreach (var action in actions)
       action(); 
}

这里的重要区别是它迭代枚举中定义的值而不是字典中的条目。这样,如果您在枚举中添加新标志而不将其添加到字典中,则在尝试使用新标志时会出现异常。它总是按照标志的顺序迭代。

【讨论】:

    猜你喜欢
    • 2016-03-02
    • 1970-01-01
    • 1970-01-01
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多