[Unity]コールバック呼び出し時のNullチェックの方法はどれが一番良いのか

Unity

はじめに

プログラムを作っている際によく使いますよね、コールバック関数

そこで毎回思うのが「Nullチェックのif文書くのがめんどくさい

今回はNullチェックをしつつ、簡単に記述できる方法を考えてみようと思います。

環境

  • Unity 2021.1.5f1

問題点

もし以下のようなプログラムだった場合

    void Start()
    {
        TestCallback();
    }

    public void TestCallback(System.Action callback = null)
    {
        callback();
    }

    // この書き方でも一緒.
    public void TestCallback(UnityEngine.Events.UnityAction callback = null)
    {
        callback();
    }

callbackがnullだった場合に以下の様な例外(Exception)が発生してしまいます。

NullReferenceException: Object reference not set to an instance of an object
SampleSceneController.TestCallback (System.Action callback) (at Assets/Scenes/SampleSceneController.cs:23)

もしこのサンプルのようにコールバックを使う場合、以下のようにNullチェックをいれます。

    public void TestCallback(System.Action callback = null)
    {
        if (callback != null)
        {
            callback();
        }
    }

こうすると、コールバックを設定していなかった場合でも例外を吐かなくなります。

ただ、コールバックを頻繁に使う場合に「このif文を毎回書くのがめんどくさい」です。

そこで色々なパターンを考えてみます。

Nullチェック付きでコールバックを呼ぶサンプル

if文を使う

まずは上記でも紹介したif文でチェックするパターンです。コールバックを頻繁に使わないのならばこれで十分かもしれません。

    public void TestCallback(System.Action callback = null)
    {
        if (callback != null)
        {
            callback();
        }
    }

拡張クラスを用意する

拡張クラスを用意し、その中でNullチェックをしてコールバックを呼び出します。

   public void TestCallback(System.Action callback = null)
    {
        callback.SafeCall();
    }

System.Actionの拡張クラス

public static class ActionExtension
{
    public static void SafeCall(this System.Action action)
    {
        if (action != null)
        {
            action();
        }
    }

    public static void SafeCall<T>(this System.Action<T> action, T arg)
    {
        if (action != null)
        {
            action(arg);
        }
    }

    public static void SafeCall<T1, T2>(this System.Action<T1, T2> action, T1 arg1, T2 arg2)
    {
        if (action != null)
        {
            action(arg1, arg2);
        }
    }

    public static void SafeCall<T1, T2, T3>(this System.Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
    {
        if (action != null)
        {
            action(arg1, arg2, arg3);
        }
    }

}

UnityEngine.Events.UnityActionの拡張クラス

public static class UnityActionExtension
{
    public static void SafeCall(this UnityEngine.Events.UnityAction action)
    {
        if (action != null)
        {
            action();
        }
    }

    public static void SafeCall<T>(this UnityEngine.Events.UnityAction<T> action, T arg)
    {
        if (action != null)
        {
            action(arg);
        }
    }

    public static void SafeCall<T1, T2>(this UnityEngine.Events.UnityAction<T1, T2> action, T1 arg1, T2 arg2)
    {
        if (action != null)
        {
            action(arg1, arg2);
        }
    }

    public static void SafeCall<T1, T2, T3>(this UnityEngine.Events.UnityAction<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
    {
        if (action != null)
        {
            action(arg1, arg2, arg3);
        }
    }

}

Nullチェックする外部メソッドを作成する

Nullチェックをしてからメソッド呼び出す外部メソッドを作成するパターン

    public void TestCallback(System.Action callback = null)
    {
        SystemUtility.SafeCall(callback);
    }

外部メソッド例

public static class SystemUtility
{
    public static void SafeCall(System.Action action)
    {
        if (action != null)
        {
            action();
        }
    }
}

これ、作るくらいなら拡張クラスのほうがわかりやすい気がします。

null 条件演算子を使ってからInvokeで呼ぶ

これが自分的には一番おすすめで、簡潔な気がします。

    public void TestCallback(System.Action callback = null)
    {
        callback?.Invoke();
    }

この書き方だと拡張クラス等なにも準備しなくてすむので、一番ラクですね。

さいごに

一番のおすすめは「null条件演算子を使ってからInvokeで呼ぶ」パターンですが、古いUnityだとC#のバージョンの制限により使うことができません。

その場合は、拡張クラスを使う方法が良いと思います。

おすすめ参考書

コメント

タイトルとURLをコピーしました