web-dev-qa-db-ja.com

可能なすべての組み合わせを宣言せずに列挙型(フラグ属性付き)をオンにしますか?

flags属性が設定されている(またはより正確にはビット操作に使用されている)enumをオンにするにはどうすればよいですか?

宣言された値と一致するスイッチですべてのケースをヒットできるようにしたい。

問題は、次の列挙型がある場合

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

このようなスイッチを使いたい

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

「theCheckType」がCheckType.Form |の両方に設定されている場合CheckType.TempData両方のケースに当てはまるようにします。私の例では、ブレークのために明らかに両方がヒットしませんが、それ以外は、CheckType.FormがCheckType.Formと等しくないために失敗します。 CheckType.TempData

私が見ることができる唯一の解決策は、列挙値の可能なすべての組み合わせについてケースを作ることです?

何かのようなもの

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

しかし、それは非常に望ましいことではありません(すぐに非常に大きくなるため)。

現在、代わりに3つのIf条件が直後にあります

何かのようなもの

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

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

....

しかし、これは、20の値を持つ列挙型がある場合、スイッチを使用する場合のように、必要な「ケース」のみに「ジャンプ」する代わりに、毎回20 If条件を通過する必要があることも意味します。

この問題を解決する魔法の解決策はありますか?

宣言された値をループしてからスイッチを使用する可能性を考えましたが、宣言された値ごとにスイッチにヒットするだけですが、それがどのように機能するかわかりません。多くのifと比較して)?

宣言されたすべての列挙値をループする簡単な方法はありますか?

私は、ToString()を使用して "、"で分割することしかできません。その後、配列をループして、すべての文字列を解析します。


更新:

説明するのに十分な仕事をしていないようです。私の例は単純です(私のシナリオを簡略化するために試みました)。

Asp.net MVCのActionMethodSelectorAttributeに使用して、URL /ルートを解決するときにメソッドを使用できるかどうかを判断します。

メソッドでこのようなものを宣言することでそれを行います

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

つまり、FormまたはTempDataに、メソッドを使用できるように指定されたキーがあるかどうかを確認する必要があります。

それが呼び出すメソッド(前の例ではdoSomething()、doSomethingElse()およびdoWhatever())は実際にはブール値を戻り値として持ち、パラメーター(次のようなインターフェイスを共有しない異なるコレクション)で呼び出されます使用-下のリンクなどで私のサンプルコードを参照してください)。

うまくいけば、私がやっていることのより良いアイデアを与えるために、私が実際に私がペーストビンでやっていることの簡単な例を貼り付けました-それはここにあります http://Pastebin.com/m478cc2b8

45
MartinF

これはどう。もちろん、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;
                }
            }
        }
    }
}
45
Jamie Ide

フラグ列挙型は、個々のビットがフラグ付き値の1つに対応する単純な整数型として扱うことができます。このプロパティを利用して、ビットフラグ付きの列挙値をブール値の配列に変換し、相関するデリゲートの配列から必要なメソッドをディスパッチできます。

編集:LINQといくつかのヘルパー関数を使用すると、このコードをよりコンパクトにすることができますが、あまり洗練されていない形式で理解する方が簡単だと思います。これは、保守性が優雅さよりも優先される場合です

次に例を示します。

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

HasFlag を使用するだけです

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

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]();
}

(テストされていないコード)

4
Rauhotz

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

C#7で可能であるべき

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

編集と実際のコードに基づいて、おそらく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;
}
1
LukeH