Включить Enum (с атрибутом 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.TempDatap >
Единственное решение, как я вижу, это сделать случай для каждой возможной комбинации значений enum?
Что-то вроде
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 Если условия сразу после eachother вместо
Что-то вроде
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)?
Есть ли простой способ перебрать все объявленные значения перечисления?
Я могу только придумать использование ToString() и разбить на ",", а затем пропустить массив и проанализировать каждую строку.
UPDATE:
Я вижу, что я не очень хорошо объяснил работу.
Мой пример - простой (попытался упростить мой сценарий).
Я использую его для ActionMethodSelectorAttribute в MVC Asp.net, чтобы определить, должен ли быть доступен метод при разрешении URL-адреса/маршрута.
Я делаю это, объявляя что-то вроде этого в методе
[ActionSelectorKeyCondition(CheckType.Form | CheckType.TempData, "SomeKey")]
public ActionResult Index()
{
return View();
}
Это означало бы, что он должен проверить, имеет ли форма или TempData ключ, указанный для способа, который будет доступен.
Методы, которые он будет вызывать (doSomething(), doSomethingElse() и doWhatever() в моем предыдущем примере) на самом деле будут иметь значение bool как возвращаемое значение и будут вызываться с параметром (разные коллекции, не разделяющие интерфейс которые можно использовать - см. мой пример кода в ссылке ниже и т.д.).
Чтобы надеяться дать лучшее представление о том, что я делаю, я вставил простой пример того, что я на самом деле делаю на pastebin - его можно найти здесь http://pastebin.com/m478cc2b8
Ответы
Ответ 1
Как насчет этого. Конечно, аргументы и возвращаемые типы 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;
}
}
}
}
}
Ответ 2
Перечисления флагов можно рассматривать как простой интегральный тип, в котором каждый отдельный бит соответствует одному из отмеченных значений. Вы можете использовать это свойство, чтобы преобразовать значение перечисления с битами в массив логических значений, а затем отправить методы, которые вам нужны, из коррелированного массива делегатов.
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 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
}
Ответ 3
Как насчет 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]();
}
(непроверенный код)
Ответ 4
Основываясь на вашем редактировании и вашем реальном коде, я бы, вероятно, обновил метод 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;
}
Ответ 5
Просто используйте HasFlag
if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...);
if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...);
if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...);
Ответ 6
Самый простой способ - просто выполнить перечисление 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;
}
Значительно чище, и вам не нужно использовать оператор if
с HasFlag
. Вы можете сделать любые комбинации, которые хотите, а затем упростить чтение оператора switch.
Я бы рекомендовал разбить ваш enums
, попробуйте посмотреть, не смешиваете ли вы разные вещи в одном и том же enum
. Вы можете настроить несколько enums
, чтобы уменьшить количество случаев.