余談: ジェネリック型に対する型パターン

Ver. 7.1

C# 7.0の時点では、ジェネリクスが絡む場合、 例えば以下のようなコードはコンパイル エラーになっていました。 (ジェネリックな型Tの変数に対してswitchできない。ちなみに、一度objectにキャストすればできる。)

static void M<T>(T x)
{
    switch (x)
    {
        case int i:
            break;
        case string s:
            break;
    }
}

Tintstringとして処理できない」と言った旨のコンパイル エラーが出ます。

さらにいうと、以下のような需要が結構ありそうな場面でも、C# 7.0ではコンパイル エラーになりました。

class Base { }
class Derived1 : Base { }
class Derived2 : Base { }
class Derived3 : Base { }

// こういう、型制約付きのやつですら 7.0 ではダメだった
static void N<T>(T x)
    where T : Base
{
    switch (x)
    {
        case Derived1 d:
            break;
        case Derived2 d:
            break;
        case Derived3 d:
            break;
    }
}

C# 7.0でも、以下のように、as演算子を使った場合にはちゃんとコンパイルできます。 型パターンは、内部的にはas演算子に展開される機能で、as演算子にできて型パターンにできないことがあるのは不自然です。

static void N<T>(T x)
    where T : Base
{
    { var d = x as Derived1; if (d != null) { return; } }
    { var d = x as Derived2; if (d != null) { return; } }
    { var d = x as Derived3; if (d != null) { return; } }
}

そこで、C# 7.1では、上記コードのような、ジェネリックな型に対する型パターンを使えるようになりました。 (新機能というよりは、仕様漏れ・バグ修正の類です。)

余談: ジェネリック型に対する is null

Ver. 8.0

C# 8.0 から、 以下のコードがコンパイルできるようになりました。

static bool M<T>(T x) => x is null;

元々 x == null であればコンパイルできていたのに、x is null がコンパイルできないのは変だということで修正されました。 型引数 T非 null 値型の時には常に false になります。

更新履歴

ブログ