C# 在不声明所有可能的组合的情况下打开枚举(带有Flags属性)?

C# 在不声明所有可能的组合的情况下打开枚举(带有Flags属性)?,c#,enums,switch-statement,flags,bit,C#,Enums,Switch Statement,Flags,Bit,如何打开设置了flags属性(或者更准确地说是用于位操作)的枚举 我希望能够在一个与声明的值匹配的开关中点击所有案例 问题是如果我有以下枚举 [Flags()]public enum CheckType { Form = 1, QueryString = 2, TempData = 4, } 我想用这样的开关 switch(theCheckType) { case CheckType.Form: DoSomething(/*Some type

如何打开设置了flags属性(或者更准确地说是用于位操作)的枚举

我希望能够在一个与声明的值匹配的开关中点击所有案例

问题是如果我有以下枚举

[Flags()]public enum CheckType
{
    Form = 1,   
    QueryString = 2,
    TempData = 4,
}
我想用这样的开关

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;
}
如果“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...
但这并不是很理想(因为它会很快变得很大)

现在,我有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 */);
}

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

有什么神奇的办法可以解决这个问题吗

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

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

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


更新:

我看我解释得不够好。 我的示例非常简单(试图简化我的场景)

我将其用于Asp.net MVC中的ActionMethodSelectorAttribute,以确定在解析url/路由时是否应该使用某个方法

我是通过在方法上声明类似这样的内容来实现的

[ActionSelectorKeyCondition(CheckType.Form | CheckType.TempData, "SomeKey")]
public ActionResult Index()
{
    return View();
} 
这意味着它应该检查表单或TempData是否具有为该方法指定的键

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


为了更好地了解我在做什么,我在pastebin上粘贴了一个简单的示例,说明我实际上在做什么-可以在这里找到一本你喜欢的
字典怎么样

dict.Add(CheckType.Form, DoSomething);
dict.Add(CheckType.TempDate, DoSomethingElse);
...
你价值的分解

flags = Enum.GetValues(typeof(CheckType)).Where(e => (value & (CheckType)e) == (CheckType)e).Cast<CheckType>();

(代码未测试)

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

编辑:我们当然可以通过使用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
  }
}
[Flags()]公共枚举检查类型
{
形式=1,
QueryString=2,
TempData=4,
}
无效性能(检查类型c)
{
//参数{c}中设置的位数组
bool[]actionMask={false,false,false};
//我们可以调用的相应操作的委托数组。。。
Action availableActions={DoSomething,DoSomethingElse,doNotherthing};
//将这些标志分解为布尔值数组
for(int i=0;iactionMask[i]=(c&(1根据您的编辑和实际代码,我可能会更新
IsValidForRequest
方法,使其看起来像这样:

public sealed override bool IsValidForRequest
    (ControllerContext cc, MethodInfo mi)
{
    _ControllerContext = cc;

    var map = new Dictionary<CheckType, Func<bool>>
        {
            { CheckType.Form, () => CheckForm(cc.HttpContext.Request.Form) },
            { CheckType.Parameter,
                () => CheckParameter(cc.HttpContext.Request.Params) },
            { CheckType.TempData, () => CheckTempData(cc.Controller.TempData) },
            { CheckType.RouteData, () => CheckRouteData(cc.RouteData.Values) }
        };

    foreach (var item in map)
    {
        if ((item.Key & _CheckType) == item.Key)
        {
            if (item.Value())
            {
                return true;
            }
        }
    }
    return false;
}
public sealed override bool IsValidForRequest
(ControllerContext cc,MethodInfo mi)
{
_ControllerContext=cc;
var-map=新字典
{
{CheckType.Form,()=>CheckForm(cc.HttpContext.Request.Form)},
{CheckType.Parameter,
()=>CheckParameter(cc.HttpContext.Request.Params)},
{CheckType.TempData,()=>CheckTempData(cc.Controller.TempData)},
{CheckType.RouteData,()=>CheckRouteData(cc.RouteData.Values)}
};
foreach(映射中的变量项)
{
if((item.Key&_CheckType)==item.Key)
{
if(item.Value())
{
返回true;
}
}
}
返回false;
}

这个怎么样。当然,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;
                }
            }
        }
    }
}
只用


最简单的方法是只执行
枚举,在您的情况下,您可以执行以下操作:

[Flags()]public enum CheckType
{
    Form = 1,   
    QueryString = 2,
    TempData = 4,
    FormQueryString = Form | QueryString,
    QueryStringTempData = QueryString | TempData,
    All = FormQueryString | TempData
}
var chkType = CheckType.Form | CheckType.QueryString;
一旦您完成了
enum
设置,现在就可以轻松地执行
开关
语句

例如,如果我设置了以下各项:

[Flags()]public enum CheckType
{
    Form = 1,   
    QueryString = 2,
    TempData = 4,
    FormQueryString = Form | QueryString,
    QueryStringTempData = QueryString | TempData,
    All = FormQueryString | TempData
}
var chkType = CheckType.Form | CheckType.QueryString;
我可以使用以下
开关
语句:

switch(chkType){
 case CheckType.Form:
   // Have Form
 break;
 case CheckType.QueryString:
   // Have QueryString
 break;
 case CheckType.TempData:
  // Have TempData
 break;
 case CheckType.FormQueryString:
  // Have both Form and QueryString
 break;
 case CheckType.QueryStringTempData:
  // Have both QueryString and TempData
 break;
 case CheckType.All:
  // All bit options are set
 break;
}
更干净,您不需要将
if
语句与
HasFlag
一起使用。您可以进行任意组合,然后使switch语句易于阅读

我建议您拆分
枚举
,试试看是否没有将不同的东西混合到同一个
枚举
。您可以设置多个
枚举
,以减少案例数量。

使用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;
    }
}
在C#7中应该是可能的


将其转换为基类型,最大的好处是当存在重复值时,它会告诉您

[Flags]
public enum BuildingBlocks_Property_Reflection_Filters
{
    None=0,
    Default=2,
    BackingField=4,
    StringAssignment=8,
    Base=16,
    External=32,
    List=64,
    Local=128,
}

switch ((int)incomingFilter)
{
    case (int)PropFilter.Default:
        break;
    case (int)PropFilter.BackingField:
        break;
    case (int)PropFilter.StringAssignment:
        break;
    case (int)PropFilter.Base:
        break;
    case (int)PropFilter.External:
        break;
    case (int)PropFilter.List:
        break;
    case (int)PropFilter.Local:
        break;
    case (int)(PropFilter.Local | PropFilter.Default):
        break;

}

谢谢你的回答。我已经更新了我原来的帖子。我不认为在属性中声明字典是可能的(参见示例)——或者至少我不知道该怎么做
switch (t1)
    {
        case var t when t.HasFlag(TST.M1):
            {
                break;
            }
        case var t when t.HasFlag(TST.M2):
            {
                break;
            }
[Flags]
public enum BuildingBlocks_Property_Reflection_Filters
{
    None=0,
    Default=2,
    BackingField=4,
    StringAssignment=8,
    Base=16,
    External=32,
    List=64,
    Local=128,
}

switch ((int)incomingFilter)
{
    case (int)PropFilter.Default:
        break;
    case (int)PropFilter.BackingField:
        break;
    case (int)PropFilter.StringAssignment:
        break;
    case (int)PropFilter.Base:
        break;
    case (int)PropFilter.External:
        break;
    case (int)PropFilter.List:
        break;
    case (int)PropFilter.Local:
        break;
    case (int)(PropFilter.Local | PropFilter.Default):
        break;

}