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


最初に

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

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

is演算子

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

// 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#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を返します。

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

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

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

//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演算子を用いた型判別&キャストを書くことができるので、適宜使用して楽をするのがいいかと思います。

最後に

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

参考