启用Enum(带Flags属性)而不声明每个可能的组合?

Mar*_*inF 44 c# enums flags bit switch-statement

我如何打开一个设置了flags属性的枚举(或更精确地用于位操作)?

我希望能够在与所声明的值匹配的开关中击中所有情况.

问题是,如果我有以下枚举

[Flags()]public enum CheckType
{
    Form = 1,   
    QueryString = 2,
    TempData = 4,
}
Run Code Online (Sandbox Code Playgroud)

我想用这样的开关

switch(theCheckType)
{
   case CheckType.Form:
       DoSomething(/*Some type of collection is passed */);
       break;

   case CheckType.QueryString:
       DoSomethingElse(/*Some other type of collection is passed */);
       break;

   case CheckType.TempData
       DoWhatever(/*Some different type of collection is passed */);
       break;
}
Run Code Online (Sandbox Code Playgroud)

如果"theCheckType"设置为CheckType.Form | CheckType.TempData我希望它能同时击中两个案例.显然,由于中断,它不会在我的示例中同时出现,但除此之外它也会失败,因为CheckType.Form不等于CheckType.Form | CheckType.TempData

那么我能看到的唯一解决方案就是为每个可能的枚举值组合做一个案例?

就像是

    case CheckType.Form | CheckType.TempData:
        DoSomething(/*Some type of collection is passed */);
        DoWhatever(/*Some different type of collection is passed */);
        break;

    case CheckType.Form | CheckType.TempData | CheckType.QueryString:
        DoSomething(/*Some type of collection is passed */);
        DoSomethingElse(/*Some other type of collection is passed */);
        break;

... and so on...
Run Code Online (Sandbox Code Playgroud)

但这真的不是很理想(因为它会很快变大)

现在我有另外3个If条件

就像是

if ((_CheckType & CheckType.Form) != 0)
{
    DoSomething(/*Some type of collection is passed */);
}

if ((_CheckType & CheckType.TempData) != 0)
{
    DoWhatever(/*Some type of collection is passed */);
}

....
Run Code Online (Sandbox Code Playgroud)

但这也意味着,如果我有一个包含20个值的枚举,它必须每次都经过20个If条件,而不是像使用开关那样"跳转"到所需的"case"/.

是否有一些神奇的解决方案来解决这个问题?

我已经考虑过循环声明值然后使用开关的可能性,然后它只会触发声明的每个值的开关,但我不知道它将如何工作以及它是否是一个好主意(相比很多if)?

是否有一种简单的方法来循环声明所有枚举值?

我只能使用ToString()并按","拆分,然后循环遍历数组并解析每个字符串.


更新:

我看到我没有做足够好的解释工作.我的例子很简单(试图简化我的场景).

我将它用于Asp.net MVC中的ActionMethodSelectorAttribute,以确定在解析url/route时是否应该有一个方法.

我通过在方法上声明类似的东西来做到这一点

[ActionSelectorKeyCondition(CheckType.Form | CheckType.TempData, "SomeKey")]
public ActionResult Index()
{
    return View();
} 
Run Code Online (Sandbox Code Playgroud)

这意味着它应检查Form或TempData是否具有为可用方法指定的密钥.

它将调用的方法(doSomething(),doSomethingElse()和doWhatever()在我之前的例子中)实际上将bool作为返回值并将使用参数调用(不同的集合不共享可以是的接口)使用 - 请参阅下面链接中的示例代码等).

为了更好地了解我在做什么,我已经粘贴了一个关于我在pastebin上实际做的简单示例 - 可以在这里找到http://pastebin.com/m478cc2b8

Jam*_*Ide 46

这个怎么样.当然,DoSomething等的参数和返回类型可以是您喜欢的任何内容.

class Program
{
    [Flags]
    public enum CheckType
    {
        Form = 1,
        QueryString = 2,
        TempData = 4,
    }

    private static bool DoSomething(IEnumerable cln)
    {
        Console.WriteLine("DoSomething");
        return true;
    }

    private static bool DoSomethingElse(IEnumerable cln)
    {
        Console.WriteLine("DoSomethingElse");
        return true;
    }

    private static bool DoWhatever(IEnumerable cln)
    {
        Console.WriteLine("DoWhatever");
        return true;
    }

    static void Main(string[] args)
    {
        var theCheckType = CheckType.QueryString | CheckType.TempData;
        var checkTypeValues = Enum.GetValues(typeof(CheckType));
        foreach (CheckType value in checkTypeValues)
        {
            if ((theCheckType & value) == value)
            {
                switch (value)
                {
                    case CheckType.Form:
                        DoSomething(null);
                        break;
                    case CheckType.QueryString:
                        DoSomethingElse(null);
                        break;
                    case CheckType.TempData:
                        DoWhatever(null);
                        break;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


LBu*_*kin 13

标志枚举可以被视为一种简单的整数类型,其中每个单独的位对应于一个标记值.您可以利用此属性将带位标记的枚举值转换为布尔数组,然后从相关的委托数组中调度您关心的方法.

编辑: 通过使用LINQ和一些辅助函数,我们当然可以使这个代码更紧凑,但我认为在不太复杂的形式中更容易理解.这可能是可维护性胜过优雅的情况.

这是一个例子:

[Flags()]public enum CheckType
{
  Form = 1,       
  QueryString = 2,
  TempData = 4,
}

void PerformActions( CheckType c )
{
  // array of bits set in the parameter {c}
  bool[] actionMask = { false, false, false };
  // array of delegates to the corresponding actions we can invoke...
  Action availableActions = { DoSomething, DoSomethingElse, DoAnotherThing };

  // disassemble the flags into a array of booleans
  for( int i = 0; i < actionMask.Length; i++ )
    actionMask[i] = (c & (1 << i)) != 0;

  // for each set flag, dispatch the corresponding action method
  for( int actionIndex = 0; actionIndex < actionMask.Length; actionIndex++ )
  {
      if( actionMask[actionIndex])
          availableActions[actionIndex](); // invoke the corresponding action
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您评估的顺序无关紧要,这里更简单,更清晰的解决方案也可以.如果顺序很重要,请将位移操作替换为包含要按其评估顺序的标记的数组:

int flagMask = 1 << 31; // start with high-order bit...
while( flagMask != 0 )   // loop terminates once all flags have been compared
{
  // switch on only a single bit...
  switch( theCheckType & flagMask )
  {
   case CheckType.Form:
     DoSomething(/*Some type of collection is passed */);
     break;

   case CheckType.QueryString:
     DoSomethingElse(/*Some other type of collection is passed */);
     break;

   case CheckType.TempData
     DoWhatever(/*Some different type of collection is passed */);
     break;
  }

  flagMask >>= 1;  // bit-shift the flag value one bit to the right
}
Run Code Online (Sandbox Code Playgroud)


Sti*_*pen 10

只需使用HasFlag

if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...);
if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...);
if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...);
Run Code Online (Sandbox Code Playgroud)


jus*_*god 7

在 C# 7 中应该是可能的

switch (t1)
    {
        case var t when t.HasFlag(TST.M1):
            {
                break;
            }
        case var t when t.HasFlag(TST.M2):
            {
                break;
            }
Run Code Online (Sandbox Code Playgroud)

  • 这仍然只会命中 1 个 case,并且你不能省略break (2认同)

Rau*_*otz 5

怎么样,Dictionary<CheckType,Action>你会像这样填充

dict.Add(CheckType.Form, DoSomething);
dict.Add(CheckType.TempDate, DoSomethingElse);
...
Run Code Online (Sandbox Code Playgroud)

你的价值的分解

flags = Enum.GetValues(typeof(CheckType)).Where(e => (value & (CheckType)e) == (CheckType)e).Cast<CheckType>();
Run Code Online (Sandbox Code Playgroud)

进而

foreach (var flag in flags)
{
   if (dict.ContainsKey(flag)) dict[flag]();
}
Run Code Online (Sandbox Code Playgroud)

(代码未经测试)


Ale*_*éau 5

使用 C# 7,您现在可以编写如下内容:

public void Run(CheckType checkType)
{
    switch (checkType)
    {
        case var type when CheckType.Form == (type & CheckType.Form):
            DoSomething(/*Some type of collection is passed */);
            break;

        case var type when CheckType.QueryString == (type & CheckType.QueryString):
            DoSomethingElse(/*Some other type of collection is passed */);
            break;

        case var type when CheckType.TempData == (type & CheckType.TempData):
            DoWhatever(/*Some different type of collection is passed */);
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不会只命中一个案例并跳过处理其余部分吗?例如,如果你有`CheckType.Form | CheckType.QueryString`,它将匹配您的 `switch` 上的第一个表达式,然后不处理 `QueryString` 逻辑。 (4认同)
  • @julealgon 是的,但这就是开关盒的工作原理。如果需要,您可以省略break语句。 (2认同)