131 lines
5.0 KiB
C#

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<Registration>
{
public static bool Match(Type openGenericType) => openGenericType == typeof(IEnumerable<>) ||
openGenericType == typeof(IReadOnlyList<>);
public List<Registration>.Enumerator GetEnumerator() => registrations.GetEnumerator();
IEnumerator<Registration> IEnumerable<Registration>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Type ImplementationType { get; }
public IReadOnlyList<Type> InterfaceTypes => interfaceTypes;
public Lifetime Lifetime => Lifetime.Transient; // Collection reference is transient. So its members can have each lifetimes.
public Type ElementType { get; }
readonly List<Type> interfaceTypes;
readonly List<Registration> registrations = new List<Registration>();
public CollectionInstanceProvider(Type elementType)
{
ElementType = elementType;
ImplementationType = elementType.MakeArrayType();
interfaceTypes = new List<Type>
{
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<RegistrationElement>.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<RegistrationElement> 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<RegistrationElement> 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;
}
}
}
}