1. ホーム
  2. c#

[解決済み] スイッチオンタイプ」これ以上の代案はないのでしょうか?

2022-03-14 09:29:05

質問

C#では switch をType上で使用することができます。 is リレーションは、複数の異なる case が適用されるかもしれません)、これ以外にタイプの切り替えをシミュレートする良い方法はないでしょうか?

void Foo(object o)
{
    if (o is A)
    {
        ((A)o).Hop();
    }
    else if (o is B)
    {
        ((B)o).Skip();
    }
    else
    {
        throw new ArgumentException("Unexpected type: " + o.GetType());
    }
}

解決方法は?

型の切り替えは、C#では確実に不足している( UPDATE: C#7 / VS 2017では、型の切り替えがサポートされています -。 Zachary Yatesの回答を参照 ). 大きなif/else文なしにこれを行うには、別の構造で作業する必要があります。TypeSwitchの構造を構築する方法については、しばらく前にブログ記事を書きました。

https://docs.microsoft.com/archive/blogs/jaredpar/switching-on-types

ショートバージョン。TypeSwitchは冗長なキャストを防ぎ、通常のswitch/case文と同様の構文になるように設計されている。例えば、標準的なWindowsのフォームイベントにおけるTypeSwitchの動作は以下のとおりです。

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

TypeSwitchのコードは、実はとても小さく、簡単にプロジェクトに入れることができます。

static class TypeSwitch {
    public class CaseInfo {
        public bool IsDefault { get; set; }
        public Type Target { get; set; }
        public Action<object> Action { get; set; }
    }

    public static void Do(object source, params CaseInfo[] cases) {
        var type = source.GetType();
        foreach (var entry in cases) {
            if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
                entry.Action(source);
                break;
            }
        }
    }

    public static CaseInfo Case<T>(Action action) {
        return new CaseInfo() {
            Action = x => action(),
            Target = typeof(T)
        };
    }

    public static CaseInfo Case<T>(Action<T> action) {
        return new CaseInfo() {
            Action = (x) => action((T)x),
            Target = typeof(T)
        };
    }

    public static CaseInfo Default(Action action) {
        return new CaseInfo() {
            Action = x => action(),
            IsDefault = true
        };
    }
}