using System; using System.Collections.Generic; using System.Linq; using Reflex.Exceptions; using Reflex.Extensions; using Reflex.Generics; using Reflex.Injectors; using Reflex.Logging; using Reflex.Resolvers; namespace Reflex.Core { public sealed class Container : IDisposable { public static Container RootContainer { get; internal set; } public string Name { get; } public Container Parent { get; } internal List Children { get; } = new(); internal Dictionary> ResolversByContract { get; } internal DisposableCollection Disposables { get; } #if UNITY_EDITOR internal static readonly List RootContainers = new(); #endif internal Container(string name, Container parent, Dictionary> resolversByContract, DisposableCollection disposables) { Diagnosis.RegisterBuildCallSite(this); Name = name; Parent = parent; Parent?.Children.Add(this); ResolversByContract = resolversByContract; Disposables = disposables; OverrideSelfInjection(); #if UNITY_EDITOR if (parent == null) { RootContainers.Add(this); } #endif } public bool HasBinding() { return HasBinding(typeof(T)); } public bool HasBinding(Type type) { return ResolversByContract.ContainsKey(type); } public void Dispose() { foreach (var child in Children.Reversed()) { child.Dispose(); } Parent?.Children.Remove(this); ResolversByContract.Clear(); Disposables.Dispose(); ReflexLogger.Log($"Container {Name} disposed", LogLevel.Info); } public Container Scope(Action extend = null) { var builder = new ContainerBuilder().SetParent(this); extend?.Invoke(builder); var scoped = builder.Build(); return scoped; } public T Construct() { return (T)Construct(typeof(T)); } public object Construct(Type concrete) { var instance = ConstructorInjector.Construct(concrete, this); AttributeInjector.Inject(instance, this); return instance; } public object Resolve(Type type) { if (type.IsEnumerable(out var elementType)) { return All(elementType).CastDynamic(elementType); } var resolvers = GetResolvers(type); var lastResolver = resolvers.Last(); var resolved = lastResolver.Resolve(this); return resolved; } public TContract Resolve() { return (TContract)Resolve(typeof(TContract)); } public object Single(Type type) { return GetResolvers(type).Single().Resolve(this); } public TContract Single() { return (TContract)Single(typeof(TContract)); } public bool TryGetResolver(Type contract, out IResolver result) { if (ResolversByContract.TryGetValue(contract, out var resolvers)) { result = resolvers.Single(); return true; } result = null; return false; } public bool TryGetResolver(out IResolver result) { return TryGetResolver(typeof(TContract), out result); } public IEnumerable All(Type contract) { return ResolversByContract.TryGetValue(contract, out var resolvers) ? resolvers.Select(resolver => resolver.Resolve(this)).ToArray() : Enumerable.Empty(); } public IEnumerable All() { return ResolversByContract.TryGetValue(typeof(TContract), out var resolvers) ? resolvers.Select(resolver => (TContract) resolver.Resolve(this)).ToArray() : Enumerable.Empty(); } private IEnumerable GetResolvers(Type contract) { if (ResolversByContract.TryGetValue(contract, out var resolvers)) { return resolvers; } throw new UnknownContractException(contract); } private void OverrideSelfInjection() { ResolversByContract[typeof(Container)] = new List { new SingletonValueResolver(this) }; } } }