C# 在不声明所有可能的组合的情况下打开枚举(带有Flags属性)?
如何打开设置了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()]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;i actionMask[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;
}