web-dev-qa-db-ja.com

タイプc#のスイッチケース

可能性のある複製:
C#-「スイッチオンタイプ」に対するこれよりも良い代替手段はありますか?

こんにちは、クラスタイプで大きなif/elseを取得するとします。スイッチケースでそれを行う方法はありますか?

例:

function test(object obj)
{
if(obj is WebControl)
{

}else if(obj is TextBox)
{

}
else if(obj is ComboBox)
{

}

など...

私は次のようなものを作りたいです

switch(obj)
{
case is TextBox:
break;
case is ComboBox:
break;

}

}

56
Cédric Boivin

番号。

http://blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx

C#言語への追加について多くのリクエストが寄せられていますが、今日は、より一般的なものの1つであるスイッチオンタイプについて説明します。タイプの切り替えは、非常に便利で簡単な機能のように見えます。値ではなく、式のタイプを切り替えるスイッチのような構造を追加します。これは次のようになります。

switch typeof(e) { 
        case int:    ... break; 
        case string: ... break; 
        case double: ... break; 
        default:     ... break; 
}

この種のステートメントは、ディスジョイント型の型階層、または所有していない型を含む型階層でのディスパッチなどの仮想メソッドを追加するのに非常に役立ちます。このような例を見ると、この機能は簡単で便利だと簡単に結論付けることができます。 「なぜ、これらの#*&%$怠zyなC#言語デザイナーが私の生活を楽にし、このシンプルで時間を節約する言語機能を追加しないのか」と考えるかもしれません。

残念ながら、多くの「単純な」言語機能のように、タイプスイッチは最初に現れるほど単純ではありません。トラブルは、次のようなより重要で重要な例を見ると始まります。

class C {}
interface I {}
class D : C, I {}

switch typeof(e) {
case C: … break;
case I: … break;
default: … break;
}

リンク: https://blogs.msdn.Microsoft.com/peterhal/2005/07/05/many-questions-switch-on-type/

C#7を更新する

はい: ソース

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}
66
Steve

次のコードは、実際の型だけを見る型スイッチ(たとえば、GetType()によって返されるもの)を想定しているため、多かれ少なかれ機能します。

public static void TestTypeSwitch()
{
    var ts = new TypeSwitch()
        .Case((int x) => Console.WriteLine("int"))
        .Case((bool x) => Console.WriteLine("bool"))
        .Case((string x) => Console.WriteLine("string"));

    ts.Switch(42);     
    ts.Switch(false);  
    ts.Switch("hello"); 
}

これが機能するために必要な機械です。

public class TypeSwitch
{
    Dictionary<Type, Action<object>> matches = new Dictionary<Type, Action<object>>();
    public TypeSwitch Case<T>(Action<T> action) { matches.Add(typeof(T), (x) => action((T)x)); return this; } 
    public void Switch(object x) { matches[x.GetType()](x); }
}
54
cdiggins

はい、名前を切り替えることができます...

switch (obj.GetType().Name)
{
    case "TextBox":...
}
24
Timothy Khouri

これは、タイプをオンに切り替えることができるというOPの要件を満たすことができる、真実のままのオプションです。十分に目を細めると、実際のswitchステートメントのように見えます。

呼び出しコードは次のようになります。

var @switch = this.Switch(new []
{
    this.Case<WebControl>(x => { /* WebControl code here */ }),
    this.Case<TextBox>(x => { /* TextBox code here */ }),
    this.Case<ComboBox>(x => { /* ComboBox code here */ }),
});

@switch(obj);

上記の各ラムダのxは強く型付けされています。キャストは必要ありません。

そして、この魔法を機能させるには、次の2つの方法が必要です。

private Action<object> Switch(params Func<object, Action>[] tests)
{
    return o =>
    {
        var @case = tests
            .Select(f => f(o))
            .FirstOrDefault(a => a != null);

        if (@case != null)
        {
            @case();
        }
    };
}

private Func<object, Action> Case<T>(Action<T> action)
{
    return o => o is T ? (Action)(() => action((T)o)) : (Action)null;
}

あなたの目にはほとんど涙が出ますよね?

それにもかかわらず、それは動作します。楽しい。

13
Enigmativity

最も簡単なことは、ダイナミクスを使用することです。つまり、Yuval Peledの回答のような単純なメソッドを定義します。

void Test(WebControl c)
{
...
}

void Test(ComboBox c)
{
...
}

コンパイル時にオーバーロード解決が行われるため、Test(obj)を直接呼び出すことはできません。オブジェクトをダイナミックに割り当ててから、Testメソッドを呼び出す必要があります。

dynamic dynObj = obj;
Test(dynObj);