| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- 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<TContract, TResult>();
- // public void AddSingle<TContract, TResult>();
- // public ServiceScopeMode GetScopeMode<TContract>();
- // public T Get<T>();
- // }
- public static class ServiceContainer
- //: IServiceContainer
- {
- static ServiceScopeMode DefaultScopeMode => Single;
- internal static Dictionary<Type, object> 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<OrderDIAttribute>()?.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<T>(Type declaringType = null)
- {
- var inst = Get(typeof(T), declaringType);
- return (T)inst;
- }
- public static ServiceScopeMode GetScopeMode<TContract>(Type declaringType = null)
- {
- var info = GetInfoWithCache(typeof(TContract), declaringType);
- return info.scopeMode;
- }
- /// <summary>
- /// for members declared in type:<typeparamref name="T"/> ...
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns></returns>
- public static BindDeclaringContext In<T>()
- {
- return In(typeof(T));
- }
- /// <summary>
- /// for members declared in <paramref name="targetType"/> ...
- /// </summary>
- /// <param name="targetType"></param>
- /// <returns></returns>
- public static BindDeclaringContext In(Type targetType)
- {
- return new BindDeclaringContext(targetType);
- }
- /// <summary>
- /// for any members with type:<typeparamref name="T"/> ...
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns></returns>
- public static BindSourceContext<T> Bind<T>()
- {
- return new BindDeclaringContext(null).Bind<T>(); // any declaring type
- }
- }
- public struct BindDeclaringContext
- {
- Type declaringType;
- internal BindDeclaringContext(Type declaringType)
- {
- this.declaringType = declaringType;
- }
- /// <summary>
- /// for members with type <typeparamref name="TSource"/>...
- /// </summary>
- /// <typeparam name="TSource"></typeparam>
- /// <returns></returns>
- public BindSourceContext<TSource> Bind<TSource>()
- {
- return new BindSourceContext<TSource>(declaringType);
- }
- }
- public struct BindSourceContext<TSource>
- {
- 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;
- }
- /// <summary>
- /// returns a new instance when get
- /// </summary>
- /// <returns></returns>
- public BindSourceContext<TSource> AsTransient()
- {
- this.scopeMode = ServiceScopeMode.Transient;
- return this;
- }
- /// <summary>
- /// return the singleton when get
- /// </summary>
- /// <param name="noLazy">instantiate immediately</param>
- /// <returns></returns>
- public BindSourceContext<TSource> AsSingle(bool noLazy = false)
- {
- this.scopeMode = ServiceScopeMode.Single;
- this.noLazy = noLazy;
- return this;
- }
- /// <summary>
- /// bind members with type <typeparamref name="TSource"/> to type <typeparamref name="TDest"/>
- /// </summary>
- /// <param name="arguments"></param>
- /// <typeparam name="TDest"></typeparam>
- public void To<TDest>(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<TDest>(Func<TDest> 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<object> creator;
- }
- }
|