using System.Collections.Concurrent;
using System.Collections.Generic;
private static RegistrationCache cache = new RegistrationCache();
static void Main(string[] args)
private static void WriteResult(string title, bool result)
Console.Write(title + ": ");
Console.WriteLine(result);
private static void DryTest()
Console.WriteLine("Dry Test:");
var container = new Container(rules => rules
.WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace)
.With(FactoryMethod.ConstructorWithResolvableArguments)
.WithoutEagerCachingSingletonForFasterAccess());
RegisterSingleton(container, typeof(Class));
RegisterMapping(container, typeof(Interface), typeof(Class));
RegisterMapping(container, typeof(Interface2), typeof(Class));
RegisterSingleton(container, typeof(Generic<>));
RegisterMapping(container, typeof(IGeneric<>), typeof(Generic<>));
RegisterMapping(container, typeof(IGeneric2<>), typeof(Generic<>));
Console.WriteLine("Registrations:");
foreach (var registration in container.GetServiceRegistrations())
Console.WriteLine($"{registration.ServiceType?.Name ?? "<null>"} => {registration.ImplementationType?.Name ?? "<null>"} (reuse: {registration.Factory.Reuse?.GetType().Name ?? "<null>"})");
Console.WriteLine("Cached registrations:");
foreach (var kv in cache.Cache)
Console.WriteLine($"{kv.Key} => {kv.Value}");
var instance1 = container.Resolve<Class>();
var instance2 = container.Resolve<Interface>();
var instance3 = container.Resolve<Interface2>();
var ginstance1 = container.Resolve<Generic<int>>();
var ginstance2 = container.Resolve<IGeneric<int>>();
var ginstance3 = container.Resolve<IGeneric2<int>>();
"Resolves same instance",
ReferenceEquals(instance1, instance2)
ReferenceEquals(instance2, instance3)
"[GEN] Resolves same instance",
ReferenceEquals(ginstance1, ginstance2)
ReferenceEquals(ginstance2, ginstance3)
RemoveInstance(container, typeof(Class));
RemoveInstance(container, typeof(Generic<>));
var instance4 = container.Resolve<Class>();
var instance5 = container.Resolve<Interface>();
var instance6 = container.Resolve<Interface2>();
var ginstance4 = container.Resolve<Generic<int>>();
var ginstance5 = container.Resolve<IGeneric<int>>();
var ginstance6 = container.Resolve<IGeneric2<int>>();
"Class resolves different instance after remove",
!ReferenceEquals(instance1, instance4));
"[GEN] Class resolves different instance after remove",
!ReferenceEquals(ginstance1, ginstance4));
"Interface resolves different instance after remove",
!ReferenceEquals(instance2, instance5));
"[GEN] Interface resolves different instance after remove",
!ReferenceEquals(ginstance2, ginstance5));
"But the same instance for different mappings again",
ReferenceEquals(instance4, instance5)
ReferenceEquals(instance5, instance6)
"[GEN] But the same instance for different mappings again",
ReferenceEquals(ginstance4, ginstance5)
ReferenceEquals(ginstance5, ginstance6)
private static void RemoveInstance(Container container, Type type)
container.RegisterMany(cache.GetFromReverseCache(type).ToArray(), type, Reuse.Singleton, setup: Setup.With(asResolutionCall: true));
private static void RegisterSingleton(Container container, Type type)
container.Register(type, Reuse.Singleton, setup: Setup.With(asResolutionCall: true));
cache.RecordMapping(type, type);
private static void RegisterMapping(Container container, Type from, Type to)
var newMappings = new HashSet<Type>(cache.GetFromReverseCache(to)) { from };
container.RegisterMany(newMappings.ToArray(), to, Reuse.Singleton, setup: Setup.With(asResolutionCall: true));
cache.RecordMapping(from, to);
public interface Interface { }
public interface Interface2 { }
public class Class : Interface, Interface2 { }
public interface IGeneric<T> { }
public interface IGeneric2<T> { }
public class Generic<T> : IGeneric<T>, IGeneric2<T> { }
public class RegistrationCache
private readonly ConcurrentDictionary<Type, Type> cache = new ConcurrentDictionary<Type, Type>();
private readonly ConcurrentDictionary<Type, HashSet<Type>> reverseCache = new ConcurrentDictionary<Type, HashSet<Type>>();
public IReadOnlyDictionary<Type, Type> Cache => this.cache;
public IReadOnlyCollection<Type> GetFromReverseCache(Type key)
return this.reverseCache[key];
public void RecordMapping(Type from, Type to)
this.reverseCache.AddOrUpdate(
_ => new HashSet<Type> { from },