59 votes

Activer Enum (avec attribut Flags) sans déclarer toutes les combinaisons possibles?

comment puis-je passer sur un enum qui ont l'attribut flags (ou plus précisément est utilisé pour les opérations sur les bits) ?

Je veux être en mesure de frapper tous les cas dans un switch qui correspond à la valeur déclarée.

Le problème est que si j'ai l'enum ci-dessous

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

et je veux utiliser un commutateur, comme ce

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

Si "theCheckType" est définie à la fois CheckType.Forme | CheckType.TempData je le veux pour frapper les deux cas. Évidemment, il l'habitude de frapper les deux dans mon exemple du fait de la rupture, mais d'autres que c'est aussi ne pas parce que CheckType.La forme n'est pas égal à CheckType.Forme | CheckType.TempData

La seule solution que je vois c'est de faire un cas pour toutes les combinaisons possibles des valeurs enum ?

Quelque chose comme

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

Mais cela n'est pas très recherchée (comme il va rapidement grandir très grand)

Droit maintenant, j'ai 3 Si les conditions de droit après l'autre à la place

Quelque chose comme

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

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

....

Mais cela signifie aussi que si j'ai un enum avec 20 valeurs, il faut aller à travers 20 Si les conditions à chaque fois au lieu de "sauter" pour seulement les "cas"/'s que lors de l'utilisation d'un commutateur.

Est-il une solution magique pour résoudre ce problème ?

J'ai pensé à la possibilité d'une boucle sur les valeurs déclarées et ensuite utiliser le commutateur, il ne frappez l'interrupteur pour chaque valeur déclarée, mais je ne sais pas comment il fonctionne et si les performances vice est une bonne idée (par rapport à beaucoup de si) ?

Est-il un moyen facile de parcourir toutes les valeurs enum déclaré ?

Je ne peux que venir avec l'aide ToString() et le fractionnement par "," et puis la boucle à travers la matrice et analyser chaque chaîne unique.


Mise à JOUR:

Je vois que je n'ai pas fait un assez bon travail avec des explications sur. Mon exemple est simple (essayé de simplifier mon scénario).

Je l'utilise pour un ActionMethodSelectorAttribute dans Asp.net MVC afin de déterminer si une méthode doit être disponible lors de la résolution de l'url/route.

Je le fais en déclarant quelque chose comme ceci sur la méthode

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

Cela signifie qu'il doit vérifier si le Formulaire ou TempData avoir une clé comme indiqué pour la méthode à être disponibles.

Les méthodes qu'il va appeler (doSomething(), doSomethingElse() et doWhatever() dans mon exemple précédent) ont en fait un bool comme valeur de retour, et sera appelé avec un paramètre (différentes collections qui ne marche pas à part une interface qui peut être utilisé - voir mon exemple de code dans le lien ci-dessous, etc).

Nous l'espérons vous donnera une meilleure idée de ce que je fais, j'ai collé un exemple simple de ce que je suis en train de faire sur pastebin - il peut être trouvé ici http://pastebin.com/m478cc2b8

60voto

Jamie Ide Points 28680

Que dis-tu de ça. Bien sûr, les arguments et les types de retour de DoSomething, etc., peuvent être ce que vous voulez.

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

14voto

LBushkin Points 60611

Drapeaux les énumérations peuvent être considérés comme une simple partie intégrante du type dans lequel chaque bit correspond à une des valeurs marquées. Vous pouvez exploiter cette propriété pour convertir les bits de pavillon valeur d'enum dans un tableau de booléens, puis expédier les méthodes que vous vous souciez d'une corrélation tableau des délégués.

EDIT: On pourrait certainement rendre ce code plus compact grâce à l'utilisation de LINQ et des fonctions d'aide, mais je pense que c'est plus facile à comprendre dans la forme moins avancée. Cela peut être le cas lorsque la maintenabilité des atouts de l'élégance.

Voici un exemple:

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

Alternativement, si l'ordre dans lequel vous évaluer n'est pas question, ici, est plus simple, plus claire de la solution qui fonctionne tout aussi bien. Si l'ordre a de l'importance, de remplacer le décalage de bits des opérations avec un tableau contenant les drapeaux dans l'ordre que vous souhaitez évaluer:

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
}

5voto

Rauhotz Points 3155

Qu'en est-il des Dictionary<CheckType,Action> que vous allez remplir comme

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

une décomposition de votre valeur

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

puis

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

(code non testé)

1voto

LukeH Points 110965

Sur la base de votre édition et de votre code réel, je mettrais probablement à jour la méthode IsValidForRequest afin qu'elle ressemble à ceci:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X