Developer

【初心者Unity】型変換の基本
2022.02.28
Lv1

【初心者Unity】型変換の基本

1.はじめに

本記事では、C#における型変換について解説していきます。
現在、Unityが提供している機能はジェネリクス等によって型変換を必要としないものがほとんどですが、ひと昔前の環境や外部のライブラリを使用する際には目にする機会もあるかも知れません。

Unity上で動作するサンプルコードも載せていますので、是非一緒に手を動かして確認してみてください!

2.型変換とは

あるデータ型の変数を別の型に変換する操作のことを型変換といいます。
例えば、int型(整数)の変数をdouble型(実数)に変換する、といった操作です。

型変換には、暗黙的な型変換と明示的な型変換(キャスト)の2種類があります。
また、明示的な型変換は、キャスト演算子またはas演算子で変換を行います。
以降でこれらの違いについて詳しく見ていきましょう。

3.暗黙的な型変換

暗黙的な型変換は、その名の通り、特定の演算子等を使用することなく実行される型変換のことです。
冒頭に紹介した、int型からdouble型への変換が代表的な例です。

// int型からdouble型への変換
int inum = 100;
double dnum = inum; // double型の変数にint型のデータを代入

これが可能なことは、それぞれの型の範囲を考えてみるとわかりやすいでしょう。
int型の範囲は -2,147,483,648 ~ 2,147,483,647
double型の範囲は -1.79769313486232e308 ~ 1.79769313486232e308
ですので、明らかにdouble型の扱う範囲の方が広くなっています。
つまり、int型の変数に代入可能な数値はdouble型の変数に対しても代入可能となる訳です。

反対に、double型からint型への暗黙的な変換はコンパイルエラーとなります。

// double型からint型への変換
double dnum = 100;
int inum = dnum; // コンパイルエラー

上記の例は、実質的に問題がない(「100」はint型の範囲内)ようにも見えます。
しかしながら、コンパイルでは形式的なチェックのみを行うため、具体的な数値に関係なくエラーとして処理されます。
double型からint型への変換を行いたい場合は、後述する明示的な型変換(キャスト)を行う必要があります。

もうひとつ、暗黙的な型変換の代表的な例を紹介します。

public class Script : MonoBehaviour
{
    void Start()
    {
        // 派生クラス型から基底クラス型への変換
        Human human = new Human();
        Animal animal = human; // Animal型の変数にHuman型のインスタンスを代入
    }
}

class Animal // 親クラス
{
    double height;
    double weight;
}
class Human : Animal // 子クラス
{
    string name;
    string country;
}

この例では、AnimalクラスとHumanクラスは継承関係にあります。
継承関係において、継承元となるクラスを基底クラス(スーパークラス)、継承先のクラスを派生クラス(サブクラス)と呼びますが、派生クラス型から基底クラス型への変換は暗黙的に行うことが可能です。
今回の例では、Animalクラスが基底クラス、Humanクラスが派生クラスですので、7行目でHuman型からAnimal型への変換を行うことができています。
なお、この変換を行った場合、派生クラス特有のメンバ情報(nameやcountry)にはアクセスできなくなってしまいます(データが失われる訳ではありません)。

4.明示的な型変換

上で説明したように、double型からint型への変換や、基底クラス型から派生クラス型への変換は暗黙的に行うことができません。
こうしたケースでコンパイルエラーを回避するには、明示的な型変換を行う必要があります。
明示的な型変換はキャストとも呼ばれ、以下2つの演算子によって実行します。

キャスト演算子
(変換後の型)変換対象の変数 という構文で明示的な型変換を行うことができます。
この際、変換後の型を囲んでいる括弧()がキャスト演算子です。

// double型からint型への変換
double dnum = 100.1;
int inum = (int)dnum;
Debug.Log(inum); //「100」と表示

上記の例では、キャスト演算子を使用することで、double型からint型への変換を実行しています。
この場合、int型が対応していない小数点以下の情報は切り捨てられてしまうことに注意してください。

public class Script : MonoBehaviour
{
    void Start()
    {
        // 基底クラス型から派生クラス型への変換
        Animal animal = new Human(); // Human型のインスタンスであることに注意
        Human human = (Human)animal;
    }
}

class Animal { } // 親クラス
class Human : Animal { } // 子クラス

この例では、基底クラス型から派生クラス型への変換を行っています。
参照型の場合、変換前のインスタンスが変換後のクラス型(またはその派生クラス型)である必要があります。
このルールに反する場合、コンパイルは通りますが実行時例外が発生します。

as演算子
変換対象の変数 as 変換後の型 という構文で明示的な型変換を行うことができます。
ただし、キャスト演算子との違いとして、値型の型変換には対応していない点に注意してください。
下記は、キャスト演算子で紹介した例をas演算子で書き換えたものです。

public class Script : MonoBehaviour
{
    void Start()
    {
        // 基底クラス型から派生クラス型への変換
        Animal animal = new Human(); // Human型のインスタンスであることに注意
        Human human = animal as Human;
    }
}

class Animal { } // 親クラス
class Human : Animal { } // 子クラス

5.明示的な型変換が失敗する場合

明示的な型変換は、通常はコンパイルエラーとなる記述を開発者の許諾によって無理に押し通すものです。
そのため、実行時に情報の欠落や型変換の失敗が発生する可能性があります。

情報の欠落に関しては、前項で既に触れているため説明を省略します。
double型からint型へ変換する際に、小数点以下の情報が切り捨てられてしまう処理のことです。

型変換の失敗に関しては、キャスト演算子とas演算子で動作が異なります。
以降で解説していきたいと思います。

キャスト演算子
型変換に失敗した場合、InvalidCastExceptionが発生します。

public class Script : MonoBehaviour
{
    void Start()
    {
        // 基底クラス型から派生クラス型への変換
        Animal animal = new Animal(); // Animal型のインスタンス
        Human human = (Human)animal; // InvalidCastException
        Debug.Log("hello"); // 実行されない
    }
}

class Animal { } // 親クラス
class Human : Animal { } // 子クラス

この例では、基底クラス型のインスタンスを派生クラス型へ変換しようとしているため、変換に失敗しています。
この場合、7行目で例外が発生するため、8行目の処理は実行されません。

as演算子
型変換に失敗した場合、nullに変換されます。

public class Script : MonoBehaviour
{
    void Start()
    {
        // 基底クラス型から派生クラス型への変換
        Animal animal = new Animal();
        Human human = animal as Human; // nullが代入される
        Debug.Log(human == null); //「True」と表示
    }
}

class Animal { } // 親クラス
class Human : Animal { } // 子クラス

先の例と同様、基底クラス型のインスタンスを派生クラス型へ変換しようとしているため、変換に失敗しています。
ただし、例外は発生せず、単にnullが返されるだけであるため、処理は続行されます。

6.キャスト演算子とas演算子の使い分け

値型以外の明示的な型変換を行う方法として、キャスト演算子とas演算子の2つの選択肢がありますが、これらはどのように使い分けるべきでしょうか。
整理も兼ねて、2つの演算子の特徴をまとめてみます。
ここまでで解説していない機能も記載しますが、応用的な内容になるため詳細は割愛します。

キャスト演算子
・値型にも対応
・型変換に失敗した場合、例外発生
・ユーザ定義の変換に対応

as演算子
・値型に非対応
・型変換に失敗した場合、nullを返す
・処理速度が速い

値型の型変換を行う場合は、キャスト演算子一択となります。
ですので、参照型を型変換する場合に使い分けを考える必要がありますが、基本的にはas演算子を使用する方が良いかと思います。

理由は処理速度の速さと、例外処理が不要な点です。
特に例外処理は、記述が煩雑になり可読性を下げるだけではなく、それ自体が速度劣化の大きな要因となりますので、避けられるのであれば避けた方が良いです。
今回説明を省略したユーザ定義の変換を行う場合等、特殊な用途以外でキャスト演算子を使用するメリットは特にないでしょう。

 

連載目次リンク

「初心者のための」Unityゲーム制作 目次
 

Unity実践編 - 目次リンク

実践Unityゲームプログラミング 連載目次