Review code for proper DI patterns using DryIoc. Ensures no static singletons, validates constructor injection and service lifetimes. Use when reviewing code, refactoring static access, or debugging DI issues.
This skill audits code for adherence to the game engine's dependency injection architecture using DryIoc. It ensures all services use constructor injection, identifies static singleton violations, and validates service registration patterns.
Invoke this skill when:
Program.csnew() for servicesNEVER create static singletons! All singleton instances must be registered in the DI container.
The ONLY acceptable static classes are pure constant classes:
EditorUIConstants - UI sizing and styling constantsRenderingConstants - Rendering configuration constantsEverything else uses dependency injection.
All dependencies must be injected through the primary constructor.
✅ CORRECT (Use Primary Constructor):
public class AnimationSystem(
ITextureFactory textureFactory,
IResourceManager resourceManager) : ISystem
{
// Dependencies are automatically available as private readonly fields
// Use textureFactory and resourceManager directly in methods
}
❌ FORBIDDEN - Static Singleton:
public class AnimationSystem
{
private static AnimationSystem? _instance;
public static AnimationSystem Instance => _instance ??= new AnimationSystem();
private AnimationSystem() { } // Private constructor
}
❌ FORBIDDEN - Property Injection:
public class AnimationSystem
{
public ITextureFactory TextureFactory { get; set; } // Don't use property injection!
public AnimationSystem() { }
}
❌ FORBIDDEN - Service Locator Pattern:
public class AnimationSystem
{
private readonly ITextureFactory _textureFactory;
public AnimationSystem()
{
// Don't resolve from container directly!
_textureFactory = ServiceLocator.Resolve<ITextureFactory>();
}
}
Location: Editor/Program.cs or Runtime/Program.cs
Service Lifetime Guidelines:
Example: Editor/Program.cs Registration:
// Core managers (Singleton)
container.Register<ISceneManager, SceneManager>(Reuse.Singleton);
container.Register<IProjectManager, ProjectManager>(Reuse.Singleton);
container.Register<ISelectionManager, SelectionManager>(Reuse.Singleton);
// Factories (Singleton)
container.Register<ITextureFactory, TextureFactory>(Reuse.Singleton);
container.Register<IShaderFactory, ShaderFactory>(Reuse.Singleton);
// Panels (Singleton)
container.Register<ContentBrowserPanel>(Reuse.Singleton);
container.Register<ConsolePanel>(Reuse.Singleton);
container.Register<PropertiesPanel>(Reuse.Singleton);
// Systems (Singleton)
container.Register<RenderingSystem>(Reuse.Singleton);
container.Register<AnimationSystem>(Reuse.Singleton);
// Transient services
container.Register<IValidationService, ValidationService>(Reuse.Transient);
Use interfaces for services that need abstraction, testability, or multiple implementations.
✅ USE INTERFACES FOR:
// Managers - multiple implementations or testability
public interface ISceneManager
{
Scene? ActiveScene { get; }
void LoadScene(string path);
}
public class SceneManager(IDependency dep) : ISceneManager { }
// Factories - abstraction from creation logic
public interface ITextureFactory
{
Texture2D CreateTexture(string path);
}
public class TextureFactory(ICache cache) : ITextureFactory { }
// Cross-cutting concerns - different implementations per platform
public interface IRendererAPI
{
void DrawIndexed(uint indexCount);
}
public class OpenGLRendererAPI(IContext context) : IRendererAPI { }
✅ SKIP INTERFACES FOR:
// Editor panels - concrete UI implementations
public class ConsolePanel(ILogger logger) { }
// ECS Systems - concrete game logic
public class AnimationSystem(ITextureFactory factory) : ISystem { }
// Pure data classes - no behavior to abstract
public class Transform
{
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
}
// Component Editors - concrete UI for specific components
public class TransformComponentEditor(IFieldEditor<Vector3> vector3Editor) { }
Decision Guide:
❌ FORBIDDEN:
// Service A depends on Service B
public class ServiceA
{
public ServiceA(IServiceB serviceB) { }
}
// Service B depends on Service A - CIRCULAR!
public class ServiceB
{
public ServiceB(IServiceA serviceA) { }
}
✅ SOLUTIONS:
Option 1: Extract shared dependency
public class ServiceA
{
public ServiceA(ISharedService shared) { }
}
public class ServiceB
{
public ServiceB(ISharedService shared) { }
}
Option 2: Use events for decoupling
public class ServiceA
{
public event Action<Data>? OnDataChanged;
}
public class ServiceB(IServiceA serviceA)
{
// Subscribe to events in constructor body or init method
public void Initialize()
{
serviceA.OnDataChanged += HandleDataChanged;
}
}
Option 3: Pass data directly
// Instead of injecting the whole service, pass only the data needed
public class ServiceA
{
public Data GetData() => _data;
}
public class ServiceB
{
public void ProcessData(Data data) // Method parameter, not constructor
{
// Process data without depending on ServiceA
}
}
Decision Tree - Choosing a Solution:
// Service with dependencies (using primary constructor)
public class AnimationSystem(ITextureFactory textureFactory) : ISystem
{
// Use textureFactory in methods
}
// Simple registration - DryIoc auto-resolves dependencies
container.Register<AnimationSystem>(Reuse.Singleton);
// Service needing initialization
container.Register<ISceneManager, SceneManager>(
Reuse.Singleton,
setup: Setup.With(allowDisposableTransient: true));
When reviewing code for DI compliance, follow this systematic approach:
Scan for static singletons:
grep -r "static.*Instance" --include="*.cs" Engine/ Editor/ | grep -v "Constants.cs"
Check constructor injection:
Validate registrations:
Editor/Program.cs or Runtime/Program.csVerify service lifetimes:
Test resolution:
For detailed troubleshooting steps, common error solutions, and automated validation scripts with expected outputs, see the Debugging Guide.
Quick validation commands:
grep -rn "static.*Instance.*=>" --include="*.cs" Engine/ Editor/ | grep -v "Constants.cs"grep -rn "ServiceLocator\|\.Resolve<" --include="*.cs" Engine/ Editor/grep -rn "{ get; set; }.*Factory\|{ get; set; }.*Manager" --include="*.cs" Engine/ Editor/Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer