🤪

null条件演算子(?.)が使えないオブジェクトでも1行で判定とメソッド呼び出しがしたい

2021/12/01に公開約1,600字

きっかけと短絡的解決手段の考案

これを踏みました。

https://qiita.com/shirasaya0201/items/866fb7caa58c6483f3ad

でも1行でどうしても書きたいので、こんなものを作ってみました。

public static class GenericExtension
{
    public static void Opt<T>(this T obj, Action<T> callAction)
    {
        if (obj != null)
        {
            callAction(obj);
        }
    }
}

こんな風に使えます。

// こう書きたいところが
obj?.Method();

// こうなっちゃうのを
if (obl != null)
{
   obj.Method();
}

// こう書ける
obj.Opt(o => o.Method());

?.が使えるようになるまでの繋ぎとしてはいいんじゃないでしょうか。
と思っていたんですが……。

追記と本質的解決

なんでそうなるねん!!!

と最初はブチ切れましたが、どうやら型を T で受けてしまうと、UnityEngine.Object での ==!= 演算子オーバーロードが剥がれてしまうようです。C++だと渡された型の実装を使ってくれるので失念していました。

というわけで型制約 where T : UnityEngine.Object を付けるか……とも考えましたが、欲しいのは ?. を機械的に置き換えられる機能なので、オブジェクトの型ごとに記法を変えさせるのはうまくないです。というわけで。

public static void Opt<T>(this T obj, Action<T> callAction)
{
    if (obj is UnityEngine.Object unityObj)
    {
        if (unityObj != null)
        {
            callAction(obj);
        }
    }
    else if (obj != null)
    {
        callAction(obj);
    }
}

こんな感じにしました。型判定後に再度 null チェックしないと "null" なオブジェクトが弾けないのでご注意ください。

値を返すメソッド呼び出し・プロパティアクセス用のオーバーロードも作っときましょう。

public static TResult Opt<T, TResult>(this T obj, Func<T, TResult> callFunc)
{
    if (obj is UnityEngine.Object unityObj)
    {
        if (unityObj != null)
        {
            return callFunc(obj);
        }
    }
    else if (obj != null)
    {
        return callFunc(obj);
    }

    return default;
}

めでたしめでたし。

じゃなくて、はよ ?.?? のフォローもお願いします……。

Discussion

ログインするとコメントできます