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
これはどう。もちろん、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;
}
}
}
}
}
フラグ列挙型は、個々のビットがフラグ付き値の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
}
HasFlag を使用するだけです
if(theCheckType.HasFlag(CheckType.Form)) DoSomething(...);
if(theCheckType.HasFlag(CheckType.QueryString)) DoSomethingElse(...);
if(theCheckType.HasFlag(CheckType.TempData)) DoWhatever(...);
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]();
}
(テストされていないコード)
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;
}
}
C#7で可能であるべき
switch (t1)
{
case var t when t.HasFlag(TST.M1):
{
break;
}
case var t when t.HasFlag(TST.M2):
{
break;
}
編集と実際のコードに基づいて、おそらく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;
}