打开枚举(带标志属性)没有声明每个可能的组合?

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

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

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

[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

唯一的解决scheme,然后我可以看到它是为每个可能的枚举值的组合情况?

就像是

  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... 

但是这真的不是非常期望的(因为它会很快变大)

现在我有三个条件,而不是彼此相反

就像是

 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如果条件每一次,而不是“跳”到只有需要的“案件”/使用开关时。

有没有一些神奇的解决scheme来解决这个问题?

我想过可能性,循环声明的值,然后使用开关,然后它只会打开每个值声明的开关,但我不知道它是如何工作,如果它的性能恶习是一个好主意(相比很多if)?

是否有一个简单的方法来遍历所有声明的枚举值?

我只能想出使用ToString()和“,”分裂,然后遍历数组,并parsing每一个string。


更新:

我看到我没有做足够好的工作解释。 我的例子是简单的(试图简化我的scheme)。

我将它用于Asp.net MVC中的ActionMethodSelectorAttribute,以确定在parsingurl / route时是否应该有可用的方法。

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

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

这意味着它应该检查Form或TempData是否具有指定的方法可用的键。

它将被调用的方法(doSomething(),doSomethingElse()和doWhatever()在我之前的例子中)实际上会有bool作为返回值,并且将被调用一个参数(不同的集合不共享一个接口,可以使用 – 看到我的例子代码在下面的链接等)。

为了让我更好地了解我在做什么,我贴了一个简单的例子,说明我实际上在做什么,在这里可以findhttp://pastebin.com/m478cc2b8

这个怎么样。 当然,DoSomething等的参数和返回types可以是任何你喜欢的。

 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; } } } } } 

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

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

这是一个例子:

 [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 } } 

另外,如果您评估的顺序无关紧要,这里也是更简单,更清晰的解决scheme。 如果顺序无关紧要,请按照您要在其中进行评估的顺序,用包含标志的数组replace位移操作:

 int flagValue = 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 } 

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

 dict.Add(CheckType.Form, DoSomething); dict.Add(CheckType.TempDate, DoSomethingElse); ... 

分解你的价值

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

接着

 foreach (var flag in flags) { if (dict.ContainsKey(flag)) dict[flag](); } 

(代码未经testing)

根据你的编辑和你的实际代码,我可能会更新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; } 

只要使用HasFlag

 if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...); if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...); if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...); 

最简单的方法就是执行ORed枚举,在你的情况下,你可以做到以下几点:

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

一旦你有了enum设置它现在很容易执行你的switch语句。

例如,如果我已经设置了以下内容:

 var chkType = CheckType.Form | CheckType.QueryString; 

我可以使用下面的switch语句如下:

 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; } 

更干净,你不需要使用HasFlagif语句。 你可以做任何你想要的组合,然后使开关语句容易阅读。

我会build议拆分你的enums ,试着看看你是不是把不同的东西混合到同一个enum 。 你可以设置多个enums来减less事件的数量。