我如何打开设置了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 ((_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();
}
这意味着它应该检查Form或TempData是否具有为该方法指定的键。
它将被调用的方法(在我之前的示例中为doSomething(),doSomethingElse()和doWhatever())实际上将bool作为返回值,并且将使用参数(不共享接口的不同集合进行调用)可以使用-在下面的链接中查看我的示例代码,等等。
[为了希望更好地了解我在做什么,我在粘贴框上粘贴了我实际上在做什么的简单示例-可以在这里找到http://pastebin.com/m478cc2b8
怎么样。当然,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;
}
}
}
}
}
只需使用HasFlag
if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...);
if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...);
if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...);
Flags枚举可被视为简单的整数类型,其中每个单个位对应于标记值之一。您可以利用此属性将位标记的枚举值转换为布尔数组,然后从相关的委托数组中分派您关心的方法。
EDIT: 我们当然可以通过使用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
}
}
或者,如果您评估的顺序无关紧要,那么这里的解决方案更简单,更清晰,同样适用。如果顺序很重要,请使用包含标志的数组替换位移操作,该标志的顺序如下:
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
}
您将填写的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](); }
(未经测试的代码)
使用C#7,您现在可以编写如下内容:
在C#7中应该可能
根据您的编辑和您的真实代码,我可能会更新IsValidForRequest
方法,使其看起来像这样:
将其声明为基本类型,这很妙,它告诉您何时它们是重复值。
最简单的方法是只执行一个ORed
枚举,您可以执行以下操作: