wake-up-neo.com

Enum (mit Flags-Attribut) einschalten, ohne jede mögliche Kombination zu deklarieren?

wie schalte ich eine Enumeration ein, bei der das Attribut flags gesetzt ist (oder genauer gesagt, wird es für Bitoperationen verwendet)?

Ich möchte in der Lage sein, alle Fälle in einem Schalter zu treffen, der den angegebenen Werten entspricht.

Das Problem ist, dass, wenn ich die folgende Aufzählung habe

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

und ich möchte einen Schalter wie diesen verwenden

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

Wenn "theCheckType" auf beide CheckType.Form | festgelegt ist CheckType.TempData Ich möchte, dass es beide Fälle trifft. Offensichtlich wird es in meinem Beispiel wegen der Unterbrechung nicht beides treffen, aber abgesehen davon schlägt es auch fehl, weil CheckType.Form nicht gleich CheckType.Form | ist CheckType.TempData

Wie ich sehe, ist die einzige Lösung, für jede mögliche Kombination der Aufzählungswerte einen Fall zu machen?

So etwas wie

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

Aber das ist wirklich nicht sehr erwünscht (da es schnell sehr groß wird)

Im Moment habe ich 3 If-Zustände direkt nacheinander

So etwas wie

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

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

....

Das bedeutet aber auch, dass ich, wenn ich eine Aufzählung mit 20 Werten habe, jedes Mal 20 If-Bedingungen durchlaufen muss, anstatt wie bei der Verwendung eines Schalters nur zu den erforderlichen "case"/s zu "springen".

Gibt es eine magische Lösung, um dieses Problem zu lösen?

Ich habe über die Möglichkeit nachgedacht, die deklarierten Werte zu durchlaufen und dann den Schalter zu verwenden, dann würde er nur für jeden deklarierten Wert auf den Schalter drücken, aber ich weiß nicht, wie es funktionieren wird und ob es eine gute Idee ist, die Leistung zu verbessern ( im vergleich zu vielen ifs)?

Gibt es eine einfache Möglichkeit, alle angegebenen Enum-Werte zu durchlaufen?

Ich kann nur ToString () verwenden und durch "," teilen und dann das Array durchlaufen und jeden einzelnen String analysieren.


AKTUALISIEREN:

Ich sehe, dass ich nicht gut genug gearbeitet habe, um zu erklären. Mein Beispiel ist zu einfach (versucht mein Szenario zu vereinfachen).

Ich verwende es für ein ActionMethodSelectorAttribute in Asp.net MVC, um zu bestimmen, ob beim Auflösen der URL/Route eine Methode verfügbar sein soll.

Ich mache das, indem ich so etwas in der Methode erkläre

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

Dies würde bedeuten, dass überprüft werden sollte, ob das Formular oder die TempData einen Schlüssel haben, der für die verfügbare Methode angegeben ist.

Die Methoden, die es aufruft (doSomething (), doSomethingElse () und doWhatever () in meinem vorherigen Beispiel), haben tatsächlich bool als Rückgabewert und werden mit einem Parameter aufgerufen (verschiedene Sammlungen, die keine Schnittstelle teilen, die es sein kann) gebraucht - siehe meinen Beispielcode im Link unten etc).

Um hoffentlich eine bessere Vorstellung davon zu bekommen, was ich tue, habe ich ein einfaches Beispiel eingefügt, was ich tatsächlich auf Pastebin tue - es ist hier zu finden http://Pastebin.com/m478cc2b8

43
MartinF

Wie wäre es damit. Natürlich können die Argumente und Rückgabetypen von DoSomething usw. beliebig sein.

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

Flags-Enums können als einfacher ganzzahliger Typ behandelt werden, bei dem jedes einzelne Bit einem der gekennzeichneten Werte entspricht. Sie können diese Eigenschaft ausnutzen, um den mit Bits gekennzeichneten Aufzählungswert in ein Array von Booleans zu konvertieren und anschließend die gewünschten Methoden aus einem korrelierten Array von Delegaten zu senden. 

EDIT: Wir könnten diesen Code durch die Verwendung von LINQ und einigen Hilfsfunktionen sicherlich kompakter machen, aber ich denke, es ist in der weniger komplexen Form leichter zu verstehen. Dies kann der Fall sein, wenn Wartbarkeit Eleganz übertrifft.

Hier ist ein Beispiel:

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

Alternativ, wenn die Reihenfolge, in der Sie auswerten, keine Rolle spielt, gibt es eine einfachere und klarere Lösung, die genauso gut funktioniert. Wenn die Reihenfolge von Bedeutung ist, ersetzen Sie die Bitverschiebungsoperationen durch ein Array, das die Flags in der Reihenfolge enthält, in der Sie sie auswerten möchten:

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

Verwenden Sie einfach HasFlag

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

Was ist mit einem Dictionary<CheckType,Action>, den Sie gerne füllen würden?

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

eine Zersetzung Ihres Wertes

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

und dann

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

(Code nicht getestet)

4
Rauhotz

Mit C # 7 können Sie jetzt so etwas schreiben: 

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

Basierend auf Ihrer Bearbeitung und Ihrem echten Code würde ich wahrscheinlich die IsValidForRequest-Methode so aktualisieren, dass sie etwa so aussieht:

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

Sollte in C # 7 möglich sein

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