web-dev-qa-db-fra.com

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

comment puis-je activer une énumération ayant l'attribut flags (ou plus précisément utilisée pour les opérations sur les bits)?

Je veux être capable de frapper tous les cas dans un commutateur qui correspond aux valeurs déclarées.

Le problème est que si j'ai l'énumération suivante

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

et je veux utiliser un commutateur comme celui-ci 

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éfini sur CheckType.Form | CheckType.TempData Je veux qu'il frappe les deux cas. Évidemment, il ne touchera pas les deux dans mon exemple à cause de la rupture, mais à part cela, il échouera également car CheckType.Form n'est pas égal à CheckType.Form | CheckType.TempData

La seule solution, comme je peux le voir, consiste donc à justifier chaque combinaison possible 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 ce n’est vraiment pas très désiré (car il va vite devenir très gros)

En ce moment j'ai 3 Si conditions juste après les autres à 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 doit passer par 20 conditions Si chaque fois au lieu de "sauter" vers les "cas"/requis nécessaires comme lors de l'utilisation d'un commutateur.

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

J'ai pensé à la possibilité de parcourir les valeurs déclarées, puis d'utiliser le commutateur. Le commutateur ne serait alors activé que pour chaque valeur déclarée, mais je ne sais pas comment cela fonctionnera et si le rendement est une bonne idée ( comparé à beaucoup de si)?

Existe-t-il un moyen facile de parcourir toutes les valeurs enum déclarées?

Je ne peux qu'utiliser ToString () et scinder en "," puis parcourir le tableau et analyser chaque chaîne.


METTRE À JOUR:

Je constate que je n’ai pas suffisamment expliqué le nombre de personnes qui ont participé à mon projet. C'est un exemple trop simple (j'ai 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/de la 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 voudrait dire qu'il devrait vérifier si le formulaire ou TempData ont une clé spécifiée pour que la méthode soit disponible. 

Les méthodes qu'il appellera (doSomething (), doSomethingElse () et doW Whatever () dans mon exemple précédent) auront en réalité bool comme valeur de retour et seront appelées avec un paramètre (différentes collections ne partageant pas une interface qui peut être utilisé - voir mon exemple de code dans le lien ci-dessous, etc.).

Si tout va bien, pour donner 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 - vous pouvez le trouver ici http://Pastebin.com/m478cc2b8

43
MartinF

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;
                }
            }
        }
    }
}
43
Jamie Ide

Les énumérations de drapeaux peuvent être traitées comme un type intégral simple dans lequel chaque bit correspond à l'une des valeurs marquées. Vous pouvez exploiter cette propriété pour convertir la valeur d'énumération avec indicateur binaire en un tableau de booléens, puis répartir les méthodes qui vous intéressent à partir d'un tableau corrélé de délégués. 

EDIT: Nous pourrions certainement rendre ce code plus compact grâce à l'utilisation de LINQ et de certaines fonctions d'aide, mais je pense que c'est plus facile à comprendre sous une forme moins sophistiquée. Cela peut être le cas où la facilité d'entretien prime sur 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( actionMask[actionIndex])
          availableActions[actionIndex](); // invoke the corresponding action
  }
}

Sinon, si l'ordre dans lequel vous évaluez n'a pas d'importance, voici une solution plus simple, plus claire et qui fonctionne aussi bien. Si l'ordre est important, remplacez les opérations de transfert de bits par un tableau contenant les indicateurs dans l'ordre dans lequel vous souhaitez les évaluer:

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
}
13
LBushkin

Il suffit d'utiliser HasFlag

if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...);
if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...);
if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...);
5
Stijn Van Antwerpen

Qu'en est-il d'un Dictionary<CheckType,Action> que vous remplirez 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>();

et alors

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

(code non testé)

4
Rauhotz

Avec C # 7, vous pouvez maintenant écrire quelque chose comme ceci: 

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;
    }
}
3
Alex Sanséau

Sur la base de votre modification et de votre code réel, je mettrais probablement à jour la méthode IsValidForRequest pour donner à peu près l'apparence suivante:

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

Devrait être possible en C # 7

switch (t1)
    {
        case var t when t.HasFlag(TST.M1):
            {
                break;
            }
        case var t when t.HasFlag(TST.M2):
            {
                break;
            }
0
justromagod