打开枚举(带标志属性)没有声明每个可能的组合?
我如何打开一个具有标志属性设置的枚举(或更精确地用于位操作)?
我希望能够在与所声明的值相匹配的开关中击中所有情况。
问题是,如果我有以下枚举
[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; }
更干净,你不需要使用HasFlag
的if
语句。 你可以做任何你想要的组合,然后使开关语句容易阅读。
我会build议拆分你的enums
,试着看看你是不是把不同的东西混合到同一个enum
。 你可以设置多个enums
来减less事件的数量。