C#のasとisと普通のキャストに関する使い分けのすゝめ (C#7.0対応)

#最初に

この記事はC#における**is演算子**と**as演算子**を使い分ける状況を考えていく内容となっております。
サンプルコードの実行環境としては**Mono 5.16.0**(C# 7.3サポート)となっております。

## is演算子とas演算子と普通のキャストの違い

### is演算子

C# 7.0以前では与えられた変数が指定の型にキャスト可能かどうかを調べるための型判定機能として使用されていました。

``` C#
// C#7.0以前

    class Animal
    {
       public int age = 0;
    }

    class Human:Animal
    {
        // なんかデータ
    }

    class Hoge
    {
        public void Piyo(Object obj)
        {
            // obj は Animalクラスを継承しているか
            if (obj is Animal)
            {
                // ここ注目!!!!!!!!!!!!!!!!!!!!
                Animal animal = (Animal)obj;

                // なんかの処理

                Console.WriteLine(animal.age);
            }
            else
            {
                Console.WriteLine(false);
            }
        }
    }
```

C#7.0からはis演算子を使ってキャストが行えるようになりました。これによって、C#6以前で必要となっていた**nullチェックが不要**になりました。

``` C#
// C#7.0以降

    class Hoge
    {
        public void Piyo(Object obj)
        {
            // ここ注目!!!!!!!!!!!!!!!!!!!!
            // obj が Animal クラスにキャストできた場合はキャスト結果を animal という変数に入れる
            // obj が null の場合はfalseとなって中の処理はもちろんキャストも通らない
            if (obj is Animal animal)
            {
                // なんかの処理
                Console.WriteLine(animal.age);
            }
            else
            {
                Console.WriteLine(false);
            }
        }
    }
```

### as演算子

普通のキャストのように使用できます。ただし普通のキャストではキャストができなかった場合には例外を返すのに対し、as演算子によるキャストでは**null**を返します。

まずは普通にキャストを使った場合のサンプルコードです。普通のキャストは値型、参照型問わず使用することができます。

``` C#
// 普通のキャストの場合
    class Huga
    {
        public void Mohu(Object obj)
        {
            try
            {
                // ここ注目!!!!!!!!!!!!!!!!!!!!
                Animal animal = (Animal)obj;
                Console.WriteLine(animal.age);
            }
            // ここ注目!!!!!!!!!!!!!!!!!!!!
            // キャストできなかった場合の例外はInvalidCastException
            catch (InvalidCastException)
            {
                Console.WriteLine(false);
            }
        }
    }
```

さて、次にas演算子を用いたキャストについてのサンプルコードです。as演算子は参照型への変換のみに使用することが可能です。

``` C#
//as演算子を用いたキャストの場合
    class Huga
    {
        public void Humo(Object obj)
        {
            // ここ注目!!!!!!!!!!!!!!!!!!!!
            Animal animal = obj as Animal;

            if(animal == null)
            {
                Console.WriteLine("動物継承してないね");
            }
            else
            {
                Console.WriteLine(animal.age);
            }
        }
    }
```

シンプルなコードですが、考えるんじゃなく感じてくだされば幸いです。

## これらのキャストの使い分け

基本的にバグやエラーの原因を突き止めるために例外を発生させるので、キャストの用途によって使い分けを考えるべきです。

例えば、**必ずキャストができる**又は**失敗した場合にプログラムをフリーズさせる**ことが前提条件とされたキャストの場合は普通の丸括弧を使用するキャストを使って例外をcatchさせるのが良いかと思います。

**null許容型を用いた処理のような、キャスト後がnullでも問題ない**場合はas演算子を使用するとスッキリとコードが書けます。

処理速度に関しては、C#で例外を発生させるのはとてもコストがかかる処理なため、基本的にasキャストの方が単なるキャストと比べて**1割程度処理速度が速い**です。ただし先述した**必ずキャストができる**ことを前提とした箇所でのキャストはasキャストよりも高速となります。

is演算子の内部としてはas演算子と同じものになるようなので、おそらく処理速度も変わらないかと思います(今回は検証していません)。is演算子によるキャストはis演算子が元々持っていた性質を使用して**switch内でのcase文にis演算子を用いた型判別&キャストを書くことができる**ので、適宜使用して楽をするのがいいかと思います。

# 最後に
一人アドカレ二日目にして颯爽と遅刻しました、ごめんなさい。

# 参考

- [現状においてC#のベースを完全に理解できる本のすゝめ](https://qiita.com/4_mio_11/items/8cf866d2243c90e341cb)
- [C#におけるasによるキャストと()によるキャストの違い](https://qiita.com/toshi0607/items/ec7f8f04f2453423d56f)
- [[雑記] キャストと as](https://ufcpp.net/study/csharp/misc_as.html)