Inversion provides field injection via inject<T>() and inject_all<T>() functions. These functions resolve dependencies during object construction when called within field initializers. The injection context is automatically established during container resolution.
Inversion
inject<T>() → TResolves a single dependency of type T. Returns null if resolution fails or no injection context exists.
public class UserService : Object {
private ILogger logger = inject<ILogger>();
public void greet(string name) {
this.logger.log(@"Hello, $name!");
}
}
inject_all<T>() → Lot<T>Resolves all registered implementations of type T. Returns empty collection if resolution fails.
public class MultiHandler : Object {
private Lot<IHandler> handlers = inject_all<IHandler>();
public void process_all(string request) {
handlers.iterate((h) => h.handle(request));
}
}
try_inject<T>() → T?Attempts to resolve a single dependency. Returns null if no injection context exists or resolution fails. Use when dependency is optional.
public class OptionalDeps : Object {
private ICache? cache = try_inject<ICache>(); // May be null
}
try_inject_all<T>() → Lot<T>Attempts to resolve all implementations. Returns empty collection if no injection context exists.
public class OptionalHandlers : Object {
private Lot<IHandler> handlers = try_inject_all<IHandler>();
}
Scope.resolve<T>()create_with_injection_context() establishes thread-local contextinject<T>() which accesses current contextInjectionContext ClassHolds the current injection state:
| Property | Type | Description |
|---|---|---|
registration |
Registration |
Current registration being resolved |
requested_type |
Type |
Type requested for resolution |
scope |
Scope |
Active scope for resolution |
error |
Error? |
Error if resolution failed |
stack_registrations |
HashSet<Registration> |
Registrations in current stack |
previous_context |
InjectionContext? |
Parent context for nested resolutions |
get_injection_context() → InjectionContextGets the current injection context. Throws error if not initialized.
try_get_injection_context() → InjectionContext?Gets current context or null if not available.
The injection context tracks the resolution stack to detect circular dependencies:
// This will throw ContainerError.CYCLE_DETECTED
public class CircularA : Object {
private CircularB b = inject<CircularB>();
}
public class CircularB : Object {
private CircularA a = inject<CircularA>();
}
Error message includes the cycle path:
Cycle detected during dependency injection: CircularA => CircularB => CircularA ad infinitum.
Dependencies can have their own injected dependencies:
public class ApiService : Object {
private Controller controller = inject<Controller>(); // Controller has its own injections
}
public class Controller : Object {
private ILogger logger = inject<ILogger>();
private IRepository repo = inject<IRepository>();
}
Injection contexts are thread-local. Each thread has its own context stack, enabling concurrent resolution in different threads.
Use inject<T>() for required dependencies:
public class OrderService : Object {
private IOrderRepository orders = inject<IOrderRepository>();
private IPaymentGateway payments = inject<IPaymentGateway>();
}
Use try_inject<T>() for optional dependencies:
public class CacheService : Object {
private ICache? cache = try_inject<ICache>();
public string? get(string key) {
if (cache == null) return null;
return cache.get(key);
}
}
Use inject_all<T>() for plugin/handler patterns:
public class ValidationService : Object {
private Lot<IValidator> validators = inject_all<IValidator>();
public bool validate(Object obj) {
foreach (var v in validators) {
if (!v.validate(obj)) return false;
}
return true;
}
}
Field injection in Inversion uses inject<T>() and inject_all<T>() functions in field initializers to resolve dependencies automatically during container-managed object creation, with cycle detection and thread-local context management.