Skip to content

17. nullを回避する

Unity使いを最も多く悩ませてきたエラーといえば、間違いなく NullReferenceException: Object reference not set to an instance of an object でしょう。これは要するに値がnullである変数を参照してしまったということです。Unityを始めたての頃は特にこのエラーに悩まされてきたことでしょう。単にインスペクターにセットし忘れていただけならまだ良いのですが、どんなに原因を探して潰しても一向に消えないこともあり、そうなるともうお手上げです。一体このnullはどこで混入してしまったのか…!?

そんな皆のトラウマであるnullですが、実際こいつは昔から世界中で猛威を振るっています。nullの生みの親であるイギリスのTony Hoareはこの状況についてこのような言葉を残しました。

私はそれを10億ドルの過ちと呼んでいる。それは1965年のnull参照の発明だ。

(I call it my billion-dollar mistake. It was the invention of the null reference in 1965.)

Tony Hoare

彼は1965年、「ALGOL W」という言語の設計中に「単に実装が非常に簡単だったから」という理由で全ての参照にnullを含めることを許可してしまいました。その結果、今に至るまでnullは世界中でシステムをクラッシュさせ、莫大な経済的損失が生まれることになりました。その教訓を活かし、RustやTypeScriptのように現在主流となっている言語の多くはnull安全という仕組みを取り入れ、最初からnullを許容しないように設計されています。

非null三原則 (返さない、渡さない、代入しない)

では、我々はこの悪魔にどう立ち向かうべきでしょうか?単にあらゆる場所でnullチェックを行えば一応安全にはなりますが、これは現実的ではありません。コードの見通しも悪くなるし、nullチェックを忘れればエラーの原因になります。

一番の解決法は、そもそもnullをコードで扱わないようにすることです。すなわち、以下の非null三原則に従います。

  • nullを返さない
    • メソッドの戻り値としてnullをreturnしないこと
  • nullを渡さない
    • メソッドの引数にnullを渡さないこと
  • nullを代入しない
    • 変数にnullを代入しないこと
csharp
// ❌ Bad: nullを返す・渡す・代入する
ISkill GetSpecialSkill()
{
    if (hasSpecialSkill)
        return specialSkill;
    return null;                    // nullを返している!
}

void UseSkill()
{
    ISkill skill = GetSpecialSkill();
    if (skill != null)              // nullチェックを忘れたらそこで終わり
    {
        skill.Execute();
    }
}

void ClearWeapon()
{
    currentWeapon = null;           // nullを代入している!
}

nullの代わりとなるダミーオブジェクトを作る

では、今まで「何もない」ということを表すためにnullを渡していた場所ではどうすればいいのでしょうか。それは、nullの代わりに「何もしない」という振る舞いを持つ安全なダミーオブジェクトを作ってそれを渡すことです。そうすればnullを参照することなく、nullチェックも不要になります。

csharp
// ✅ Good: 「何もしない」ダミーオブジェクトを使う (Null Objectパターン)
public interface ISkill
{
    void Execute();
}

// 「何もしない」という振る舞いを持つダミー
public class NullSkill : ISkill
{
    public void Execute() { } // 何もしない
}

ISkill GetSpecialSkill()
{
    if (hasSpecialSkill)
        return specialSkill;
    return new NullSkill(); // nullの代わりにダミーを返す
}

void UseSkill()
{
    ISkill skill = GetSpecialSkill();
    skill.Execute(); // nullチェック不要! NullSkillなら何も起きないだけ
}

RequireComponentを使う

Unityのインスペクターでコンポーネントをアタッチし忘れた結果nullエラーが起きた経験はよくあるでしょう。そこで、Unityの [RequireComponent] 属性を使用することで、自動的にそのコンポーネントを追加してくれ、アタッチし忘れを防いでくれます。

csharp
[RequireComponent(typeof(Rigidbody))]
public class PlayerMovement : MonoBehaviour
{
    Rigidbody rb;

    void Awake()
    {
        rb = GetComponent<Rigidbody>(); // RequireComponentがあるのでnullにならない
    }
}

Unity設計講習会 資料公開ページ