views:

1016

answers:

4

Hello,

how do i switch on an enum which have the flags attribute set (or more precisly is used for bit operations) ?

I want to be able to hit all cases in a switch that matches the values declared.

The problem is that if i have the following enum

[Flags()]public enum CheckType
{
 Form = 1, 
 QueryString = 2,
 TempData = 4,
}

and i want to use a switch like this

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;
}

If "theCheckType" is set to both CheckType.Form | CheckType.TempData i want it to hit both case's. Obviously it wont hit both in my example because of the break, but other than that it also fails because CheckType.Form is not equal to CheckType.Form | CheckType.TempData

The only solution then as I can see it is to make a case for every possible combination of the enum values ?

Something like

    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...

But that really isnt very desired (as it will quickly grow very big)

Right now i have 3 If conditions right after eachother instead

Something like

if ((_CheckType & CheckType.Form) != 0)
{
    DoSomething(/*Some type of collection is passed */);
}

if ((_CheckType & CheckType.TempData) != 0)
{
    DoWhatever(/*Some type of collection is passed */);
}

....

But that also means that if i have an enum with 20 values it have to go through 20 If conditions every single time instead of "jumping" to only the needed "case"/'s as when using a switch.

Is there some magic solution to solve this problem ?

I have thought of the possibility to loop through the declared values and then use the switch, then it would only hit the switch for each value declared, but i dont know how it will work and if it performance vice is a good idea (compared to a lot of if's) ?

Is there an easy way to loop through all the enum values declared ?

I can only come up with using ToString() and splitting by "," and then loop through the array and parse every single string.


UPDATE:

I see that i havent done a good enough job explaning. My example is to simple (tried to simplify my scenario).

I use it for a ActionMethodSelectorAttribute in Asp.net MVC to determine if a method should be available when resolving the url/route.

I do it by declaring something like this on the method

[ActionSelectorKeyCondition(CheckType.Form | CheckType.TempData, "SomeKey")]
public ActionResult Index()
{
    return View();
}

That would mean that it should check if the Form or TempData have a key as specified for the method to be available.

The methods it will be calling (doSomething(), doSomethingElse() and doWhatever() in my previous example) will actually have bool as return value and will be called with a parameter (different collections that doesnt share a interface that can be used - see my example code in the link below etc).

To hopefully give a better idea of what i am doing i have pasted a simple example of what i am actually doing on pastebin - it can be found here http://pastebin.com/m478cc2b8

+2  A: 

What about a Dictionary<CheckType,Action> that you will fill like

dict.Add(CheckType.Form, DoSomething);
dict.Add(CheckType.TempDate, DoSomethingElse);
...

a decomposition of your value

flags = Enum.GetValues(typeof(CheckType)).Where(e => (value & (CheckType)e) == (CheckType)e).Cast<CheckType>();

and then

foreach (var flag in flags)
{
   if (dict.ContainsKey(flag)) dict[flag]();
}

(code untested)

Rauhotz
Thanks for the answer.I have updated my original post. I dont think it is possible to use a dictionary as i am declaring it in a attribute (see example) - or at least i dont know how it should be done.
MartinF
Maybe not adequate for MartinF's problem, but very elegant... +1 ;)
Thomas Levesque
+7  A: 

Flags enums can be treated as a simple integral type in which each individual bit corresponds to one of the flagged values. You can exploit this property to convert the bit-flagged enum value into an array of booleans, and then dispatch the methods you care about from a correlated array of delegates.

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.

Here's an example:

[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( actionFlag )
          availableActions[actionIndex](); // invoke the corresponding action
  }
}

Alternatively, if the order in which you evaluate doesn't matter, here is simpler, clearer solution that works just as well. If order does matter, replace the bit-shifting operations with an array containing the flags in the order you want to evaluate them in:

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
}
LBushkin
Very nice solution!
Henri
Thanks for the answer.I have updated my original question. It seems like a nice solution, but unfortunatly the way my code is structured i need to get a return value from the method being called and also pass different type of parameters (i have linked to an example of what i am trying to do at the bottom). Hopefully the example code will explain my problem better than my writting. :)
MartinF
A: 

Based on your edit and your real-life code, I'd probably update the IsValidForRequest method to look something like this:

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;
}
LukeH
Thanks for the answer.I dont see how this would be different from the "If" conditions.This would loop through every value in "map" (and every value in the CheckType enum would have to be registered in the "map" dictionary).If you mean that I should declare the Dictionary in the attribute, i dont think that is possible.I have updated my original question with a link to some example code of what i am actually trying to do, to hopefully clarify it better.
MartinF
@MartinF, This is based on your updated example code! You're right that it isn't vastly different to your "if" conditions. The only advantage is that any new CheckType value only has to be included in the map.
LukeH
I'm still not entirely clear on why you think your "if" statements are a problem. If you're trying to avoid them for performance reasons then that sounds very much like a premature optimisation to me.
LukeH
+4  A: 

How about this. Of course the arguments and return types of DoSomething, etc., can be anything you like.

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;
                }
            }
        }
    }
}
Jamie Ide
Thanks for your reply.It is pretty much like Lukes solution just using the switch instead of the dictionary mapping.This seems to be the best solution though i have really hoped that it was possible somehow to only loop the values in "theCheckType" instead of looping the whole enum, as there really is no reason to check all the enum values, only the ones set in "theCheckType". But i guess that isnt possible.
MartinF
I don't see the similarity to Luke's answer; I think mine is significantly simpler. As I see it, you've either got to check "theCheckType" against the enum or check the enum against "theCheckType". Either way you have to check all the possible values. If you declare your enum as long (Int64) you can obviously have a maximum of 64 values, so there's no need to worry about performance in such a short loop.
Jamie Ide
Have a look at this: http://stackoverflow.com/questions/1060760/what-to-do-when-bit-mask-flags-enum-gets-too-large
Jamie Ide
Thanks again. What i mean about the similarity to Lukes answers is that they both do the same - loop through every value in the CheckType enum, and in his case call use dictionary and yours use the switch (which is actually behind the scenes as far as i know using some sort of implementation of a hashtable / dictionary).But i agree yours is simpler and probably the best solution (will mark it as an accepted answer)If i could only loop the values in "theCheckType" i could just call the switch directly, and it would only loop and call the switch for the number of times neccesary.
MartinF