CenterBase
Coding Style wiki
CenterBase<TCenter, TClass> is the abstract registration center base class of CenterFrame. It has a built-in thread-safe singleton and a type-safe Dictionary<int, TClass> cache, providing a unified object management interface — register (Add), remove (Delete), and look up (Find). APICenter (APIBase) and EventCenter (EventBase) both build on it as their shared base, and you can also implement your own center with a custom TClass base type.
| Namespace | OxGFrame.CenterFrame |
| Type | public abstract class |
| Source | CenterBase.cs |
using OxGFrame.CenterFrame;
Declaration
public abstract class CenterBase<TCenter, TClass> where TCenter : CenterBase<TCenter, TClass>, new()
Generic Parameters
| Generic Parameter | Description |
|---|---|
| TCenter | The center subclass itself (CRTP self-referencing constraint); it must have a parameterless constructor (new()). |
| TClass | The base type of the managed objects (such as APIBase for APICenter or EventBase for EventCenter). |
When inheriting, pass the subclass itself and the managed base type as the generic parameters:
public class APICenterExample : CenterBase<APICenterExample, APIBase> { /* ... */ }
public class EventCenterExample : CenterBase<EventCenterExample, EventBase> { /* ... */ }
How It Works
Singleton & Registration Timing
CenterBase<TCenter, TClass> has a built-in thread-safe lazy singleton (double-checked locking). The first call to any Default API static method automatically creates the TCenter instance and runs its constructor; it is recommended to register your objects with Register in the subclass constructor.
Important Never call any Default API static method (such as Add / Find) inside the subclass constructor — it recurses through GetInstance() and causes a StackOverflow (infinite loop). Use the instance methods (such as Register) in the constructor instead.
ID Rules
- Each registered object is cached under a unique
intid. The generic overloads without an id usetypeof(UClass).GetHashCode()as the id (so only one instance per type can be registered this way). - The overloads with an id let you assign custom ids, allowing multiple instances of the same type.
- Registering a duplicate id logs a warning and is skipped — the existing object is not overwritten.
Default API vs Instance Methods
The static Default APIs (Add / Delete / Find) are forwarded through the singleton to the corresponding instance methods (Register / Remove / Get):
- Callers: use the Default APIs directly on the subclass name (e.g.
EventCenterExample.Find<UClass>()). - Inside the subclass (constructor): use the instance methods (e.g.
this.Register<UClass>()).
Attention CenterBase is only responsible for object registration and cache management. If a managed object holds Unity assets (such as prefabs), make sure to release them properly (in conjunction with AssetLoaders) before removal.
Inheritance Example
An EventCenter-style example with a custom event center and event class:
using Cysharp.Threading.Tasks;
using OxGFrame.CenterFrame;
using OxGFrame.CenterFrame.EventCenter;
using UnityEngine;
// Custom event center: TCenter is the subclass itself (CRTP), TClass is the managed base type
public class EventCenterExample : CenterBase<EventCenterExample, EventBase>
{
public EventCenterExample()
{
// Register events in the constructor (instance method Register only — never Default APIs here)
this.Register<EventMsgTest>();
}
}
// Custom event: inherit EventBase
public class EventMsgTest : EventBase
{
private string _message;
public void Emit(string message)
{
this._message = message;
this.HandleEvent().Forget();
}
public async override UniTaskVoid HandleEvent()
{
// Handle the event content
Debug.Log($"Received message: {this._message}");
this.Release();
}
protected override void Release()
{
// Release the data held by the event
this._message = null;
}
}
Callers look up the event through the Default API and dispatch it:
// Find the registered event by type and dispatch it
EventCenterExample.Find<EventMsgTest>()?.Emit("Hello OxGFrame");
An APICenter-style center works the same way — just use APIBase as TClass:
using OxGFrame.CenterFrame;
using OxGFrame.CenterFrame.APICenter;
// APIGetPlayerData is a custom API class inheriting APIBase
public class APICenterExample : CenterBase<APICenterExample, APIBase>
{
public APICenterExample()
{
this.Register<APIGetPlayerData>();
}
}
Reminder You can quickly create subclasses from the templates via the Project window context menu: Create → OxGFrame → Center Frame → Event Center / API Center → Template Scripts.
Member Overview
Static Methods (Default API)
Called directly on the subclass name after inheriting (e.g. EventCenterExample.Find<UClass>()).
| Method | Description |
|---|---|
| Add | Instantiates and registers an object in the cache. |
| Delete | Removes an object from the cache. |
| DeleteAll | Clears all objects in the cache. |
| Find<UClass> | Looks up an object in the cache and returns it cast to the target type. |
Instance Methods
| Method | Description |
|---|---|
| Get<UClass> | Gets an object from the cache and casts it (instance version of Find). |
| Has<UClass> | Checks whether an object is registered. |
| Register | Registers an object in the cache (instance version of Add). |
| Remove | Removes an object from the cache (instance version of Delete). |
| RemoveAll | Clears the cache (instance version of DeleteAll). |
Protected Members (available to subclasses)
| Member | Type | Description |
|---|---|---|
GetInstance() | static TCenter | Gets the center singleton (lazily created, thread-safe). |
GetFromCache(int id) | TClass | Fetches an object from the cache; if not found, logs an error and returns default. |
HasInCache(int id) | bool | Checks whether the given id exists in the cache. |
Add
public static void Add<UClass>() where UClass : TClass, new()
public static void Add<UClass>(int id) where UClass : TClass, new()
public static void Add(int id, TClass @class)
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used as the id. |
| @class | TClass | An existing object instance (registered under the given id). |
Description
Instantiates (new UClass()) and registers an object in the cache, or registers an existing instance under the given id.
- The generic overloads require
UClassto have a parameterless constructor (new()constraint). - Registering a duplicate id logs a warning and is skipped; the existing object is not overwritten.
Example
// Register with the type hash as the id
EventCenterExample.Add<EventMsgTest>();
// Register an existing instance under a custom id
EventCenterExample.Add(0x01, new EventMsgTest());
Delete
public static void Delete<UClass>() where UClass : TClass
public static void Delete(int id)
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used for the lookup. |
Description
Removes a registered object from the cache; does nothing if not found.
DeleteAll
public static void DeleteAll()
Description
Clears all registered objects in the cache.
Find<UClass>
public static UClass Find<UClass>() where UClass : TClass
public static UClass Find<UClass>(int id) where UClass : TClass
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used for the lookup. |
Returns
UClass — the matching object in the cache (cast to UClass); if not found, an error is logged and default is returned (null for reference types).
Description
Looks up a registered object in the cache and casts it automatically. The parameterless overload looks up by type hash, so it only works for objects registered without an explicit id; for objects registered with a custom id, use Find<UClass>(int id).
Example
var evt = EventCenterExample.Find<EventMsgTest>();
evt?.Emit("Hello OxGFrame");
Get<UClass>
public UClass Get<UClass>() where UClass : TClass
public UClass Get<UClass>(int eventId) where UClass : TClass
Parameters
| Parameter | Type | Description |
|---|---|---|
| eventId | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used for the lookup. |
Returns
UClass — the matching object in the cache (cast to UClass); if not found, an error is logged and default is returned.
Description
Instance version of Find<UClass>, for use inside the subclass.
Has<UClass>
public bool Has<UClass>() where UClass : TClass
public bool Has<UClass>(int id) where UClass : TClass
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used for the check. |
Returns
bool — whether the object is registered in the cache.
Description
Checks whether an object is registered. Useful before registering or looking up, to avoid the duplicate-registration warning or the not-found error log.
Register
public void Register<UClass>() where UClass : TClass, new()
public void Register<UClass>(int id) where UClass : TClass, new()
public void Register(int id, TClass @class)
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used as the id. |
| @class | TClass | An existing object instance (registered under the given id). |
Description
Instance version of Add. Use it to register objects in the subclass constructor (where static Default APIs must not be called). Registering a duplicate id logs a warning and is skipped.
Example
public EventCenterExample()
{
this.Register<EventMsgTest>();
}
Remove
public void Remove<UClass>() where UClass : TClass
public void Remove(int id)
Parameters
| Parameter | Type | Description |
|---|---|---|
| id | int | Registration id. When omitted, typeof(UClass).GetHashCode() is used for the lookup. |
Description
Instance version of Delete; does nothing if not found.
RemoveAll
public void RemoveAll()
Description
Instance version of DeleteAll — clears all registered objects in the cache.