Вопрос по c# – Включить Enum (с атрибутом Flags), не объявляя все возможные комбинации?
Как включить перечисление, для которого установлен атрибут flags (или, точнее, используется для битовых операций)?
Я хочу быть в состоянии поразить все случаи в выключателе, который соответствует заявленным значениям.
Проблема в том, что если у меня есть следующее перечисление
<code>[Flags()]public enum CheckType { Form = 1, QueryString = 2, TempData = 4, } </code>
и я хочу использовать такой переключатель
<code>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; } </code>
Если & quot; theCheckType & quot; установлен в оба CheckType.Form | CheckType.TempData Я хочу, чтобы он попадал в оба случая. Очевидно, что из-за разрыва он не будет попадать в оба примера, но кроме этого он также завершится неудачей, поскольку CheckType.Form не равен CheckType.Form | CheckType.TempData
Тогда единственное решение, как я вижу, состоит в том, чтобы обосновать каждую возможную комбинацию значений enum?
Что-то вроде
<code> 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... </code>
Но это на самом деле не очень желательно (так как оно быстро станет очень большим)
Прямо сейчас у меня есть 3 Если условия сразу после друг друга
Что-то вроде
<code>if ((_CheckType & CheckType.Form) != 0) { DoSomething(/*Some type of collection is passed */); } if ((_CheckType & CheckType.TempData) != 0) { DoWhatever(/*Some type of collection is passed */); } .... </code>
Но это также означает, что если у меня есть перечисление с 20 значениями, оно должно проходить через 20 условий If каждый раз вместо «прыжка». только к необходимому «случаю» / /, как при использовании переключателя.
Есть какое-то волшебное решение, чтобы решить эту проблему?
Я думал о возможности циклически проходить через объявленные значения и затем использовать переключатель, тогда он будет нажимать на переключатель только для каждого объявленного значения, но я не знаю, как это будет работать, и если это отрицательное влияние на производительность - хорошая идея ( по сравнению с большим количеством "если"?
Есть ли простой способ перебрать все объявленные значения перечисления?
Я могу только придумать, как использовать ToString () и разделить на & quot;, & quot; а затем перебрать массив и проанализировать каждую строку.
ОБНОВИТЬ:
Я вижу, что я недостаточно хорошо объяснил объяснение. Мой пример прост (попытался упростить мой сценарий).
Я использую его для ActionMethodSelectorAttribute в Asp.net MVC, чтобы определить, должен ли метод быть доступен при разрешении URL / маршрута.
Я делаю это, заявляя что-то вроде этого в методе
<code>[ActionSelectorKeyCondition(CheckType.Form | CheckType.TempData, "SomeKey")] public ActionResult Index() { return View(); } </code>
Это будет означать, что он должен проверить, есть ли у формы или TempData ключ, указанный для того, чтобы метод был доступен.
Методы, которые он будет вызывать (doSomething (), doSomethingElse () и doWhwhat () в моем предыдущем примере), на самом деле будут иметь значение bool в качестве возвращаемого значения и будут вызываться с параметром (разные коллекции, у которых нет общего интерфейса, который может быть используется - см. мой пример кода в ссылке ниже и т. д.).
Надеюсь, чтобы дать лучшее представление о том, что я делаю, я вставил простой пример того, что я на самом деле делаю на pastebin - это можно найти здесь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;
}
}
}
}
}
Перечисления флагов можно рассматривать как простой целочисленный тип, в котором каждый отдельный бит соответствует одному из отмеченных значений. Это свойство можно использовать для преобразования значения enum с помеченными битами в массив логических значений, а затем отправлять интересующие вас методы из коррелированного массива делегатов.
EDIT: We could certainly make this code more compact through the use of LINQ and some helper functions, but I think it's easier to understand in the less sophisticated form. This may be case where maintainability trumps elegance.
Вот пример:
[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
}
На основании ваших правок и вашего реального кода я, вероятно, обновлю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;
}
Самый простой способ - просто выполнитьORed
enum, в вашем случае вы можете сделать следующее:
[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
уменьшить количество дел.
Как насчет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 теперь вы можете написать что-то вроде этого:
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;
}
}