【UE5】デリゲートの登録と実行を分離する (UE5.5 -)
はじめに
Unreal Engine では、DECLARE_DELEGATE
やDECLARE_MULTICAST_DELEGATE
などのマクロを使って、簡単にイベント通知の仕組み (デリゲート) を実装することができます。
これらのデリゲートはBind〇〇()
/Unbind()
やAdd〇〇()
/Remove()
などを使って関数の登録/解除を行い、Execute()
やBroadcast()
で実行できる便利な仕組みです。UIイベントや状態通知など、様々な場面で活用されていると思います。
public:
DECLARE_DELEGATE(FSampleDelegate, int32);
FSampleDelegate SampleDelegate;
protected:
void Example()
{
// デリゲートに関数を登録
SampleDelegate.AddLambda(
[](int32 Value)
{
UE_Log(LogTemp, Log, TEXT("executed! Value=%d"), Value);
});
// デリゲートに登録された関数を実行
SampleDelegate.ExecuteIfBound(65535);
}
しかし、デリゲートを外部から登録可能にするために public なメンバとして公開してしまうと、意図せず外部から実行されてしまうリスクがあります。
「外部から登録してほしいけど、実行はしてほしくない」というケースにおいて、この点は大きな懸念事項でした。
この問題を解決するため、UE5.5 から TDelegateRegistration および TMulticastDelegateRegistration というクラスが導入されました。
これらを使うことで、外部に対して関数の登録のみを許可し、実行は制限する といった安全な設計が可能になります。
TDelegateRegistration/TMulticastDelegateRegistration
TDelegateRegistration/TMulticastDelegateRegistration は、DECLARE_DELEGATE
などで定義されるデリゲート型の基底クラスとして、 UE5.5 から導入されたクラスです。
これらは、従来のデリゲート機能から 関数の登録 のみを切り出したインターフェースとなっており、実行 (Execute
/Broadcast
) などの機能は持っていません。
これにより、デリゲートを外部に公開する際に「登録だけ許可して実行は許さない」といった意図的なアクセス制御が可能となりました。
これらの型は、デリゲートの引数の型をテンプレート引数に持つテンプレートクラスとして定義されています。
つまり TDelegateRegistration らの型を得たい場合は、面倒なテンプレート指定をする必要があります。
そこで便利なのが、デリゲート型に定義されているRegistrationType
という型エイリアスです。
これをを使うことで、テンプレート引数を明示することなく、登録専用の型を簡単に参照することができます。
public:
DECLARE_DELEGATE(FSampleDelegate, int32);
FSampleDelegate SampleDelegate;
protected:
void Example()
{
// RegistrationType エイリアスを参照すれば, デリゲートの引数の型を気にせず利用できる
FSampleDelegate::RegistrationType& SampleDelegateRegister = SampleDelegate;
SampleDelegateRegister.AddLambda(
[](int32 Value)
{
UE_Log(LogTemp, Log, TEXT("executed! Value=%d"), Value);
});
}
これからのデリゲートの公開
登録用にデリゲートを外部に公開する際は、TDelegateRegistration/TMulticastDelegateRegistration 型として参照を公開すると良いでしょう。
デリゲートを「誰が実行するべきか」という責務を明確に分離でき、より堅牢な設計になります。
public:
DECLARE_DELEGATE(FSampleDelegate, int32);
// UE5.5 以前
public:
FSampleDelegate SampleDelegate; // 外部からExecute()できてしまう
// UE5.5 以降
private:
FSampleDelegate SampleDelegatePrivate; // 外部から実行できないよう隠蔽
public:
// 登録用に TDelegateRegistration 型で公開
FSampleDelegate::RegistrationType& SampleDelegate()
{
return SampleDelegatePrivate;
}
Discussion