LoginSignup
3
4

Source GeneratorでNullObjectを自動生成する

Last updated at Posted at 2023-12-18

この記事はUnity Advent Calendar 2023 19日目の記事です。
昨日は【AudioMixer】exposed parametersの操作クラスを自動生成するでした。
明日は@Teach さんの Unity と toio と M5AtomS3 を Bluetoothで繋げた話 です!

また、同じくSourceGenerator関係の記事としてSource Generatorでプロパティを自動実装してみたも投稿しているので、よろしければご覧ください!

NullObjectとは

NullObjectは Nullチェックを減らすためのデザインパターン です。

提供側がNullのかわりに、何もしないオブジェクトを渡すことで、
利用者側がNullエラーのことを考えずに使えるようにしよう、という考え方です!

これで憎き NullExceptionSay Good-bye!!!

色々な解説サイトや本に詳しい説明があるため、詳細は割愛しますが、
FactoryパターンやDIコンテナと組み合わせると、地味ながら縁の下の力持ち的な存在になります!

私はAdaptive Codeで知りました。
interfaceと併せた便利な使い方も詳述されており、オススメです!

NullObjectの欠点

とても良い考え方なのですが、
作るのがめんどくさい!

そう、作るのがめんどくさいのです!

自動生成しよう!

そういうわけで、自動生成機能を作りました!

導入方法

上記URLよりDLLをダウンロードし、Unity内に放り込み、設定することで利用できます!
導入方法については、以下記事に詳しく書いておきました!
DL用のURLだけ上記のURLに差し替えてお考えください!

Unityは Unity 2021.3 以上、
IDEは以下条件で動作するはずです。

JetBrains Rider Editor v3.0.9以降
Code Editor Package for Visual Studio v2.0.11以降
Code Editor Package for Visual Studio Code v1.2.4以降

利用しているIDEのバージョンによっては追加工程が必要な場合があります(URL内詳述)。

使い方

2つの機能を持っているので、順に解説します!

指定クラスの全てのInterfaceを継承したNullObjectを作る

一つ目の機能は、Classにアトリビュートをつけて利用します。
アトリビュート名は InheritsToNullObj です。
利用例は以下の通りです。

using NullObjectGenerator;

    [InheritsToNullObj]
    public class TestClass : IHogeInterface , IFugaInterface
    {
        //略
    }

その名の通り、
対象クラスが継承している全ての interface を継承した、NullObectを生成します。

生成されたクラスの名前は、元々のクラス名の末尾にAsNullObjを付したものになります。
上記の例の場合、TestClassAsNullObjという名前のクラスが生成されます。

interfaceによって指定されているプロパティ、メソッドは自動実装します。
返り値がある場合、defaultを返しますが、
返り値がUniTaskの場合のみ、UniTask.CompletedTaskを返します。

対象になるのはInterfaceのみです。abstractクラスは対象外ですのでご注意ください。

指定したInterfaceを一つだけ実装したNullObjectを作る

二つ目の機能は、interfaceにアトリビュートをつけて利用します。
アトリビュート名は InterfaceToNullObj です。
利用例は以下の通りです。

using NullObjectGenerator;

    [InterfaceToNullObj]
    public interface IHogeInterface
    {
        //略
    }

生成されたクラスの名前は、元々のクラス名の末尾にAsNullObjを付したものになります。
上記の例の場合、IHogeInterfaceAsNullObjという名前のクラスが生成されます。

生成するのはクラスなのに I から始まっているのはちょっとキモチワルイですが、
AsNullObjと合わせるならこのままかなと思い、一旦そのままにしています。
要望があれば変更します…!

interfaceによって指定されているプロパティ、メソッドは自動実装します。
返り値がある場合、defaultを返しますが、
返り値がUniTaskの場合のみ、UniTask.CompletedTaskを返します。

対象になるのはInterfaceのみです。abstractクラスは対象外ですのでご注意ください。

オプション

何もしないとはいえ、さすがに履歴だけは残したい……

そんなケースに備えて、ログを出せるようにしておきました。
どちらのアトリビュートも引数でログの出し方を指定できます。

ログのタイプは以下の通りです!

    public enum LogType
    {
        None,     //デフォルト値 ログを出さない
        DebugLog, // UnityEngine.Debug.Logを出す
        DebugLogErr,  //UnityEngine.Debug.LogErrorを出す
        DebugLogWarn, //UnityEngine.Debug.LogWarningを出す
        ThrowException, //System.ExceptionをThrowする
    }

使うときには、以下のように指定してください。

[InheritsToNullObj(LogType.ThrowException)]
[InterfaceToNullObj(LogType.DebugLogErr)]

おわりに

ここまでお読みいただき、ありがとうございます!

こんなものが自動生成できるとは、SourceGeneratorはなんとも便利なものです……

また、VContainerに拡張メソッドを作って、
指定クラスが見つからない場合にNullObjectを登録する改造を後日公開したいと思います!

組み合わせるとさらに便利ですので、ぜひストックして通知を受け取ってくださいね!

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4