using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace VContainer.Internal { struct RegistrationElement { public Registration Registration; public IObjectResolver RegisteredContainer; public RegistrationElement(Registration registration, IObjectResolver registeredContainer) { Registration = registration; RegisteredContainer = registeredContainer; } } sealed class CollectionInstanceProvider : IInstanceProvider, IEnumerable { public static bool Match(Type openGenericType) => openGenericType == typeof(IEnumerable<>) || openGenericType == typeof(IReadOnlyList<>); public List.Enumerator GetEnumerator() => registrations.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public Type ImplementationType { get; } public IReadOnlyList InterfaceTypes => interfaceTypes; public Lifetime Lifetime => Lifetime.Transient; // Collection reference is transient. So its members can have each lifetimes. public Type ElementType { get; } readonly List interfaceTypes; readonly List registrations = new List(); public CollectionInstanceProvider(Type elementType) { ElementType = elementType; ImplementationType = elementType.MakeArrayType(); interfaceTypes = new List { RuntimeTypeCache.EnumerableTypeOf(elementType), RuntimeTypeCache.ReadOnlyListTypeOf(elementType), }; } public override string ToString() { var contractTypes = InterfaceTypes != null ? string.Join(", ", InterfaceTypes) : ""; return $"CollectionRegistration {ImplementationType} ContractTypes=[{contractTypes}] {Lifetime}"; } public void Add(Registration registration) { foreach (var x in registrations) { if (x.Lifetime == Lifetime.Singleton && x.ImplementationType == registration.ImplementationType) { throw new VContainerException(registration.ImplementationType, $"Conflict implementation type : {registration}"); } } registrations.Add(registration); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public object SpawnInstance(IObjectResolver resolver) { if (resolver is IScopedObjectResolver scope) { using (ListPool.Get(out var entirelyRegistrations)) { CollectFromParentScopes(scope, entirelyRegistrations); return SpawnInstance(resolver, entirelyRegistrations); } } var array = Array.CreateInstance(ElementType, registrations.Count); for (var i = 0; i < registrations.Count; i++) { array.SetValue(resolver.Resolve(registrations[i]), i); } return array; } internal object SpawnInstance(IObjectResolver currentScope, IReadOnlyList entirelyRegistrations) { var array = Array.CreateInstance(ElementType, entirelyRegistrations.Count); for (var i = 0; i < entirelyRegistrations.Count; i++) { var x = entirelyRegistrations[i]; var resolver = x.Registration.Lifetime == Lifetime.Singleton ? x.RegisteredContainer : currentScope; array.SetValue(resolver.Resolve(x.Registration), i); } return array; } internal void CollectFromParentScopes( IScopedObjectResolver scope, List registrationsBuffer, bool localScopeOnly = false) { foreach (var registration in registrations) { registrationsBuffer.Add(new RegistrationElement(registration, scope)); } var finderType = InterfaceTypes[0]; scope = scope.Parent; while (scope != null) { if (scope.TryGetRegistration(finderType, out var registration) && registration.Provider is CollectionInstanceProvider parentCollection) { foreach (var x in parentCollection.registrations) { if (!localScopeOnly || x.Lifetime != Lifetime.Singleton) { registrationsBuffer.Add(new RegistrationElement(x, scope)); } } } scope = scope.Parent; } } } }