using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace com.bbbirder.injection { using static ServiceScopeMode; public enum ServiceScopeMode { Single, Transient, } // public interface IServiceContainer // { // public void AddTransient(); // public void AddSingle(); // public ServiceScopeMode GetScopeMode(); // public T Get(); // } public static class ServiceContainer //: IServiceContainer { static ServiceScopeMode DefaultScopeMode => Single; internal static Dictionary singletons = new(); internal static Dictionary<(Type desiredType, Type declaringType), Info> lutInfos = new(); public static void ClearInstances() { singletons.Clear(); } public static int GetDeriveDistance(Type subType, Type baseType) { if ((baseType ?? subType) == null) return 0; if (baseType == null) return -1; if (baseType.IsInterface) { var dist = -1; while (subType != null && subType.GetInterfaces().Contains(baseType)) { subType = subType.BaseType; dist++; } return dist; } else { var dist = 0; while (subType != baseType) { if (subType is null) return -1; subType = subType.BaseType; dist++; } return dist; } } static Type FindImplementSubclass(Type type) { if (type.IsSealed) return type; var subtypes = Retriever.GetAllSubtypes(type) .Append(type) .Where(t => !t.IsInterface) .Where(t => !t.IsAbstract) .ToArray() ; if (subtypes.Length == 0) { // throw new ArgumentException($"type {type} doesn't has an implement"); return null; } if (subtypes.Length == 1) { return subtypes[0]; } var minOrdered = subtypes .GroupBy(t => t.GetCustomAttribute()?.order ?? 0) .OrderBy(g => g.Key) .First() .ToArray(); if (minOrdered.Length > 1) { minOrdered = minOrdered .GroupBy(t => GetDeriveDistance(t, type)) .OrderBy(g => -g.Key) .First() .ToArray(); } if (minOrdered.Length > 1) { DebugHelper.LogWarning($"type {type} exists more than one implements: {string.Join(",", minOrdered.Select(t => t.FullName))}"); } return minOrdered[0]; } static Type FindTargetType(Type type) { if (!type.IsGenericType) return FindImplementSubclass(type); if (type.IsGenericTypeDefinition) { throw new ArgumentException($"a unbound generic type {type}"); } var unboundType = type.GetGenericTypeDefinition(); var unboundTypeArguments = unboundType.GetGenericArguments(); var resultTypeArguments = new Type[type.GenericTypeArguments.Length]; for (var i = 0; i < type.GenericTypeArguments.Length; i++) { var typeArg = type.GenericTypeArguments[i]; var unboudnTypeArg = unboundTypeArguments[i]; var notImpl = typeArg.IsAbstract || typeArg.IsInterface; // DebugHelper.Log($"{typeArg} {unboudnTypeArg} {notImpl} {unboudnTypeArg.GenericParameterAttributes} {unboudnTypeArg.GenericParameterAttributes | GenericParameterAttributes.Covariant}"); if (notImpl && (unboudnTypeArg.GenericParameterAttributes & GenericParameterAttributes.Covariant) == 0) { throw new($"type arg {unboudnTypeArg} must has a 'out' modifier in {unboundType}"); } resultTypeArguments[i] = FindTargetType(typeArg); } var unboundResultType = FindImplementSubclass(unboundType); if (unboundResultType is null) return unboundResultType; // DebugHelper.Log($"{unboundResultType} {resultTypeArguments.Length}"); return unboundResultType.MakeGenericType(resultTypeArguments); } static Info GetInfoWithCache(Type desiredType, Type declaringType) { Info info; if (lutInfos.TryGetValue((desiredType, declaringType), out info)) return info; // first in order if (declaringType != null) { foreach (var interfType in declaringType.GetInterfaces()) { if (lutInfos.TryGetValue((desiredType, interfType), out info)) return info; } for (var curType = declaringType.BaseType; curType != null; curType = curType.BaseType) { if (lutInfos.TryGetValue((desiredType, curType), out info)) return info; } } if (lutInfos.TryGetValue((desiredType, null), out info)) return info; // default cache info.resultType = FindTargetType(desiredType); info.scopeMode = Single; // default scope mode lutInfos[(desiredType, null)] = info; return info; } static Info GetProperInfoAndCache(Type desiredType, Type declaringType) { var info = GetInfoWithCache(desiredType, declaringType); var resultType = info.resultType; if (resultType is null) { // dont cache on missing implementation return info; } if (resultType.IsAbstract || resultType.IsInterface) { var info2 = GetProperInfoAndCache(resultType, null); if (resultType == info2.resultType || info2.resultType.IsAssignableFrom(resultType)) { throw new($"find {desiredType} returns a not implemented result"); } info.resultType = info2.resultType; } return lutInfos[(desiredType, declaringType)] = info; } static object CreateInstance(Info info) { if (info.creator != null) return info.creator(); if (info.resultType == null) return null; return Activator.CreateInstance(info.resultType, info.constructorArguments); } public static object Get(Type desiredType, Type declaringType = null, bool throwOnNoImplementations = true) { var info = GetProperInfoAndCache(desiredType, declaringType); if (info.resultType is null) { if (throwOnNoImplementations) throw new ArgumentException($"type {desiredType} doesn't has an implement"); return null; } if (info.scopeMode == Single) { if (!singletons.TryGetValue(info.resultType, out var inst)) { singletons[info.resultType] = inst = CreateInstance(info); } return inst; } else { return CreateInstance(info); } } public static T Get(Type declaringType = null) { var inst = Get(typeof(T), declaringType); return (T)inst; } public static ServiceScopeMode GetScopeMode(Type declaringType = null) { var info = GetInfoWithCache(typeof(TContract), declaringType); return info.scopeMode; } /// /// for members declared in type: ... /// /// /// public static BindDeclaringContext In() { return In(typeof(T)); } /// /// for members declared in ... /// /// /// public static BindDeclaringContext In(Type targetType) { return new BindDeclaringContext(targetType); } /// /// for any members with type: ... /// /// /// public static BindSourceContext Bind() { return new BindDeclaringContext(null).Bind(); // any declaring type } } public struct BindDeclaringContext { Type declaringType; internal BindDeclaringContext(Type declaringType) { this.declaringType = declaringType; } /// /// for members with type ... /// /// /// public BindSourceContext Bind() { return new BindSourceContext(declaringType); } } public struct BindSourceContext { Type declaringType; Type desiredType; bool noLazy; ServiceScopeMode scopeMode; internal BindSourceContext(Type declaringType) { this.declaringType = declaringType; this.desiredType = typeof(TSource); this.scopeMode = ServiceScopeMode.Single; this.noLazy = false; } /// /// returns a new instance when get /// /// public BindSourceContext AsTransient() { this.scopeMode = ServiceScopeMode.Transient; return this; } /// /// return the singleton when get /// /// instantiate immediately /// public BindSourceContext AsSingle(bool noLazy = false) { this.scopeMode = ServiceScopeMode.Single; this.noLazy = noLazy; return this; } /// /// bind members with type to type /// /// /// public void To(params object[] arguments) where TDest : TSource { var typePair = (desiredType, declaringType); ServiceContainer.lutInfos[typePair] = new() { resultType = typeof(TDest), scopeMode = scopeMode, constructorArguments = arguments, creator = null, }; if (noLazy && scopeMode == ServiceScopeMode.Single) { ServiceContainer.Get(desiredType, declaringType); } } public void To(Func creator) where TDest : TSource { var typePair = (desiredType, declaringType); DebugHelper.Log($"to {scopeMode} {typeof(TDest)}"); ServiceContainer.lutInfos[typePair] = new() { resultType = typeof(TDest), scopeMode = scopeMode, // constructorArguments = arguments, creator = () => creator() }; if (noLazy && scopeMode == ServiceScopeMode.Single) { ServiceContainer.Get(desiredType, declaringType); } } } struct Info { public Type resultType; public ServiceScopeMode scopeMode; public object[] constructorArguments; public Func creator; } }