48

いくつかの変数を取り、次のような switch ステートメントを使用したいと思います。

switch (intVal1, strVal2, boolVal3)
{
   case 1, "hello", false:
      break;
   case 2, "world", false:
      break;
   case 2, "hello", false:

   etc ....
}

C# でこのようなことを行う方法はありますか? (明らかな理由から、ネストされた switch ステートメントを使用したくありません)。

この質問は、まさにこの恐れを実装することにより、.net 開発チームによって回答されました: C# の多変数スイッチ ステートメント

4

13 に答える 13

47

C# 7 以降では、次のwhenキーワードを使用してこれを行うことができます。

switch (intVal1)
{
    case 1 when strVal2 == "hello" && boolVal3 == false:
        break;
    case 2 when strVal2 == "world" && boolVal3 == false:
        break;
    case 2 when strVal2 == "hello" && boolVal3 == false:
        break;
}
于 2018-05-26T14:37:59.963 に答える
13

C# でこれを行うための組み込み機能はありません (ありませんでした)。また、これを行うためのライブラリも知りません。

Tupleおよび拡張メソッドを使用した別のアプローチを次に示します。

using System;

static class CompareTuple {
    public static bool Compare<T1, T2, T3>(this Tuple<T1, T2, T3> value, T1 v1, T2 v2, T3 v3) {
        return value.Item1.Equals(v1) && value.Item2.Equals(v2) && value.Item3.Equals(v3); 
    }
}

class Program {
    static void Main(string[] args) {
        var t = new Tuple<int, int, bool>(1, 2, false);
        if (t.Compare(1, 1, false)) {
            // 1st case
        } else if (t.Compare(1, 2, false)) {
            // 2nd case
        } else { 
            // default
        }
    }
}

ifこれは基本的に、複数の値をチェックするための便利な構文を提供し、の代わりに複数の を使用するだけswitchです。

于 2011-11-01T14:03:03.907 に答える
12

これを別の方法で見てみましょう。あなたが持っている場合:

  • チェックしたい非常に具体的な組み合わせ。
  • 比較する必要はありません。
  • 一致しないすべてのケースのデフォルト ハンドラー。
  • すべてのプリミティブ/値型 ( intboolstringなど)

次に、代わりにルックアップ テーブルを使用できます。これは、ステートメントと同様の実行速度switchですが、それほど効率的ではありません (ハッシュを計算する必要があるため)。それでも、おそらくそれで十分です。また、ケースに名前を付ける機会が与えられ、この組み合わせの爆発をやや混乱させず、維持しにくくすることができます。

コード例:

private static readonly Tuple<int, int, bool> NameOfCase1 = 
    Tuple.Create(1, 1, false);
private static readonly Tuple<int, int, bool> NameOfCase2 =
    Tuple.Create(2, 1, false);
private static readonly Tuple<int, int, bool> NameOfCase3 =
    Tuple.Create(2, 2, false);

private static readonly Dictionary<Tuple<int, int, bool>, string> Results =
    new Dictionary<Tuple<int, int, bool>, string>
{
    { NameOfCase1, "Result 1" },
    { NameOfCase2, "Result 2" },
    { NameOfCase3, "Result 3" }
};

public string GetResultForValues(int x, int y, bool b)
{
    const string defaultResult = "Unknown";
    var lookupValue = Tuple.Create(x, y, b);
    string result;
    Results.TryGetValue(lookupValue, out result);
    return defaultResult;
}

ケースごとに関数またはメソッドを実際に実行する必要がある場合は、代わりにAction<T>orの結果タイプ (辞書値) を使用できますFunc<T>

既にすべてのハッシュ コード ロジックが組み込まれているため、ここで使用していることに注意してくださいTuple<T1,T2,T3>。構文は C# では少し扱いに​​くいですが、必要に応じて独自のルックアップ クラスを実装し、 and をオーバーライドするだけEqualsですGetHashCode

于 2011-11-01T14:20:20.740 に答える
6

文字列に変換できます:

switch (intVal1.ToString() + strVal2 + boolVal3.ToString())
{
   case "1helloFalse":
      break;
   case "2worldFalse":
      break;
   case "2helloFalse":

   etc ....
}

ただし、ロジックを定義するためのより良い方法があるかどうかという問題が生じると思います。たとえば、スーパーマンを知っている人を見つけようとしているとしましょう。次のようにチェックできます。

switch (first + last)
{
   case "ClarkKent":
   case "LoisLane":
      // YES
      break;
   default;
      // Sadly, no
      break;
}

しかし、クラーク・ケントという名前の別の男を手に入れたらどうなるでしょうか? bool KnowsSuperman など、このロジックに基づいて決定する他の値を持つことはできませんか?

アイデアは、switch ステートメントを使用して、単一の選択肢セットに基づいてロジックを決定することです。切り替えようとしている値が複数ある場合、ロジックを維持するのが非常に困難になる可能性があります。

もう 1 つの例は、人々をいくつかのグループにグループ分けし、そのグループに応じて何らかのロジックを実行する必要がある場合です。たとえば、ボブ、ジェフ、ジム、またはサリーの場合は、次のようにコーディングできます。グループ A にいるのに、グループ A に他の人を追加する必要がある場合はどうすればよいでしょうか? コードを変更する必要があります。代わりに、グループと呼ばれる追加のプロパティを作成できます。これは、列挙型または文字列であり、誰かが属するグループを指定するために使用できます。

于 2011-11-01T14:48:12.293 に答える
6

これに対する私の実にクレイジーな見方:

class Program
{
    static void Main(string[] args)
    {
        var i = 1;
        var j = 34;
        var k = true;
        Match(i, j, k).
            With(1, 2, false).Do(() => Console.WriteLine("1, 2, 3")).
            With(1, 34, false).Do(() => Console.WriteLine("1, 34, false")).
            With(x => i > 0, x => x < 100, x => x == true).Do(() => Console.WriteLine("1, 34, true"));

    }

    static Matcher<T1, T2, T3> Match<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
    {
        return new Matcher<T1, T2, T3>(t1, t2, t3);
    }
}

public class Matcher<T1, T2, T3>
{
    private readonly object[] values;

    public object[] Values
    {
        get { return values; }
    }

    public Matcher(T1 t1, T2 t2, T3 t3)
    {
        values = new object[] { t1, t2, t3 };
    }

    public Match<T1, T2, T3> With(T1 t1, T2 t2, T3 t3)
    {
        return new Match<T1, T2, T3>(this, new object[] { t1, t2, t3 });
    }

    public Match<T1, T2, T3> With(Func<T1, bool> t1, Func<T2, bool> t2, Func<T3, bool> t3)
    {
        return new Match<T1, T2, T3>(this, t1, t2, t3);
    }
}

public class Match<T1, T2, T3>
{
    private readonly Matcher<T1, T2, T3> matcher;
    private readonly object[] matchedValues;
    private readonly Func<object[], bool> matcherF; 

    public Match(Matcher<T1, T2, T3> matcher, object[] matchedValues)
    {
        this.matcher = matcher;
        this.matchedValues = matchedValues;
    }

    public Match(Matcher<T1, T2, T3> matcher, Func<T1, bool> t1, Func<T2, bool> t2, Func<T3, bool> t3)
    {
        this.matcher = matcher;


        matcherF = objects => t1((T1)objects[0]) && t2((T2)objects[1]) && t3((T3)objects[2]);
    }

    public Matcher<T1, T2, T3> Do(Action a)
    {
        if(matcherF != null && matcherF(matcher.Values) || matcher.Values.SequenceEqual(matchedValues))
            a();

        return matcher;
    }
}
于 2011-11-01T14:07:46.417 に答える
1

C#言語仕様に従って、switchステートメント式はsbyte、byte、sbyte、byte、short、ushort、int、uint、long、ulong、char、string、またはenum-typeのいずれかに解決される必要があります。Tupleこれは、スイッチをオンにしたり、他の高次タイプをオンにしたりできないことを意味します。

余裕があると仮定して、値をまとめてみることができます。たとえば、各整数が0..9の範囲にあることが保証されているとします。

switch (intVal1 * 100 + intVal2 * 10 + (boolVal3 ? 1 : 0))
{
case 100: /* intVal1 = 1, intVal2 = 0, boolVal3 = false */ ... break;
case 831: /* intVal1 = 8, intVal2 = 3, boolVal3 = true */ ... break;
}
于 2011-11-01T14:02:33.597 に答える
0
if (a == 1 && b == 1) {}
else if (a == 1 && b == 2) {}
else if (a == 2 && b ==2) {}
于 2011-11-01T14:00:40.600 に答える
0

私はこの種のことをリストまたは配列で行います。可能な条件を列挙できる場合 (複数値の切り替えを行いたい場合は明らかに列挙できます)、複数部分のキーとActionorFunc<T>を値として使用してルックアップ テーブルを作成します。

単純なバージョンでは、次を使用しDictionaryます。

class LookupKey: IComparable<LookupKey>
{
    public int IntValue1 { get; private set; }
    public int IntValue2 { get; private set; }
    public bool BoolValue1 { get; private set; }
    public LookupKey(int i1, int i2, bool b1)
    {
        // assign values here
    }
    public int Compare(LookupKey k1, LookupKey k2)
    {
        return k1.IntValue1 == k2.IntValue1 &&
               k1.IntValue2 == k2.IntValue2 &&
               k1.BoolValue1 == k2.BoolValue1;
    }
    public int GetHashCode()
    {
        return (19 * IntValue1) + (1000003 * IntValue2) + (BoolValue1) ? 0 : 100000073;
    }
    // need to override Equals
}

そしてあなたの辞書:

static readonly Dictionary<LookupKey, Action<object>> LookupTable;

その後、起動時にディクショナリにデータを入力できます。その後、ルックアップは次のような単純な問題になります。

Action<object> MethodToCall;
if (LookupTable.TryGetValue(new LookupKey(i1, i2, b1), out MethodToCall)
    MethodToCall(theData);
else
    // default action if no match

設定するのは少しコードですが、実行は非常に高速です。

于 2011-11-01T14:22:28.743 に答える
0

私の知る限り、C# でそれを行うことはできません。

ただし、MSDN からこれを行うことができます。

次のサンプルは、空のケース ラベルに対して、あるケース ラベルから別のケース ラベルへのフォール スルーが許可されていることを示しています。

 switch(n) 
        {
            case 1:
            case 2: 
            case 3: 
                Console.WriteLine("It's 1, 2, or 3.");
                break; 
        default: 
            Console.WriteLine("Not sure what it is.");
            break; 
        }
于 2011-11-01T13:57:17.157 に答える
0
//.Net Core 3.1
    class Convertors
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Convertors.ConvertAny("m","cm", 10));
            Console.ReadKey();
        }
        public static double MToCM(double value)
        {
            return value * 100;
        }
        public static double ConvertAny(string srcUOM, string tgtUOM, double value)
        {
            switch (srcUOM.ToLower(), tgtUOM.ToLower())
            {
                case ("m", "cm"): return Convertors.MToCM(value);
                default: throw new NotImplementedException();
            }
        }
    }
于 2020-01-13T13:03:15.923 に答える