ServiceContainer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. namespace com.bbbirder.injection
  6. {
  7. using static ServiceScopeMode;
  8. public enum ServiceScopeMode
  9. {
  10. Single,
  11. Transient,
  12. }
  13. // public interface IServiceContainer
  14. // {
  15. // public void AddTransient<TContract, TResult>();
  16. // public void AddSingle<TContract, TResult>();
  17. // public ServiceScopeMode GetScopeMode<TContract>();
  18. // public T Get<T>();
  19. // }
  20. public static class ServiceContainer
  21. //: IServiceContainer
  22. {
  23. static ServiceScopeMode DefaultScopeMode => Single;
  24. internal static Dictionary<Type, object> singletons = new();
  25. internal static Dictionary<(Type desiredType, Type declaringType), Info> lutInfos = new();
  26. public static void ClearInstances()
  27. {
  28. singletons.Clear();
  29. }
  30. public static int GetDeriveDistance(Type subType, Type baseType)
  31. {
  32. if ((baseType ?? subType) == null) return 0;
  33. if (baseType == null) return -1;
  34. if (baseType.IsInterface)
  35. {
  36. var dist = -1;
  37. while (subType != null && subType.GetInterfaces().Contains(baseType))
  38. {
  39. subType = subType.BaseType;
  40. dist++;
  41. }
  42. return dist;
  43. }
  44. else
  45. {
  46. var dist = 0;
  47. while (subType != baseType)
  48. {
  49. if (subType is null) return -1;
  50. subType = subType.BaseType;
  51. dist++;
  52. }
  53. return dist;
  54. }
  55. }
  56. static Type FindImplementSubclass(Type type)
  57. {
  58. if (type.IsSealed) return type;
  59. var subtypes = Retriever.GetAllSubtypes(type)
  60. .Append(type)
  61. .Where(t => !t.IsInterface)
  62. .Where(t => !t.IsAbstract)
  63. .ToArray()
  64. ;
  65. if (subtypes.Length == 0)
  66. {
  67. // throw new ArgumentException($"type {type} doesn't has an implement");
  68. return null;
  69. }
  70. if (subtypes.Length == 1)
  71. {
  72. return subtypes[0];
  73. }
  74. var minOrdered = subtypes
  75. .GroupBy(t => t.GetCustomAttribute<OrderDIAttribute>()?.order ?? 0)
  76. .OrderBy(g => g.Key)
  77. .First()
  78. .ToArray();
  79. if (minOrdered.Length > 1)
  80. {
  81. minOrdered = minOrdered
  82. .GroupBy(t => GetDeriveDistance(t, type))
  83. .OrderBy(g => -g.Key)
  84. .First()
  85. .ToArray();
  86. }
  87. if (minOrdered.Length > 1)
  88. {
  89. DebugHelper.LogWarning($"type {type} exists more than one implements: {string.Join(",", minOrdered.Select(t => t.FullName))}");
  90. }
  91. return minOrdered[0];
  92. }
  93. static Type FindTargetType(Type type)
  94. {
  95. if (!type.IsGenericType) return FindImplementSubclass(type);
  96. if (type.IsGenericTypeDefinition)
  97. {
  98. throw new ArgumentException($"a unbound generic type {type}");
  99. }
  100. var unboundType = type.GetGenericTypeDefinition();
  101. var unboundTypeArguments = unboundType.GetGenericArguments();
  102. var resultTypeArguments = new Type[type.GenericTypeArguments.Length];
  103. for (var i = 0; i < type.GenericTypeArguments.Length; i++)
  104. {
  105. var typeArg = type.GenericTypeArguments[i];
  106. var unboudnTypeArg = unboundTypeArguments[i];
  107. var notImpl = typeArg.IsAbstract || typeArg.IsInterface;
  108. // DebugHelper.Log($"{typeArg} {unboudnTypeArg} {notImpl} {unboudnTypeArg.GenericParameterAttributes} {unboudnTypeArg.GenericParameterAttributes | GenericParameterAttributes.Covariant}");
  109. if (notImpl && (unboudnTypeArg.GenericParameterAttributes & GenericParameterAttributes.Covariant) == 0)
  110. {
  111. throw new($"type arg {unboudnTypeArg} must has a 'out' modifier in {unboundType}");
  112. }
  113. resultTypeArguments[i] = FindTargetType(typeArg);
  114. }
  115. var unboundResultType = FindImplementSubclass(unboundType);
  116. if (unboundResultType is null) return unboundResultType;
  117. // DebugHelper.Log($"{unboundResultType} {resultTypeArguments.Length}");
  118. return unboundResultType.MakeGenericType(resultTypeArguments);
  119. }
  120. static Info GetInfoWithCache(Type desiredType, Type declaringType)
  121. {
  122. Info info;
  123. if (lutInfos.TryGetValue((desiredType, declaringType), out info)) return info; // first in order
  124. if (declaringType != null)
  125. {
  126. foreach (var interfType in declaringType.GetInterfaces())
  127. {
  128. if (lutInfos.TryGetValue((desiredType, interfType), out info)) return info;
  129. }
  130. for (var curType = declaringType.BaseType; curType != null; curType = curType.BaseType)
  131. {
  132. if (lutInfos.TryGetValue((desiredType, curType), out info)) return info;
  133. }
  134. }
  135. if (lutInfos.TryGetValue((desiredType, null), out info)) return info; // default cache
  136. info.resultType = FindTargetType(desiredType);
  137. info.scopeMode = Single; // default scope mode
  138. lutInfos[(desiredType, null)] = info;
  139. return info;
  140. }
  141. static Info GetProperInfoAndCache(Type desiredType, Type declaringType)
  142. {
  143. var info = GetInfoWithCache(desiredType, declaringType);
  144. var resultType = info.resultType;
  145. if (resultType is null)
  146. {
  147. // dont cache on missing implementation
  148. return info;
  149. }
  150. if (resultType.IsAbstract || resultType.IsInterface)
  151. {
  152. var info2 = GetProperInfoAndCache(resultType, null);
  153. if (resultType == info2.resultType || info2.resultType.IsAssignableFrom(resultType))
  154. {
  155. throw new($"find {desiredType} returns a not implemented result");
  156. }
  157. info.resultType = info2.resultType;
  158. }
  159. return lutInfos[(desiredType, declaringType)] = info;
  160. }
  161. static object CreateInstance(Info info)
  162. {
  163. if (info.creator != null) return info.creator();
  164. if (info.resultType == null) return null;
  165. return Activator.CreateInstance(info.resultType, info.constructorArguments);
  166. }
  167. public static object Get(Type desiredType, Type declaringType = null, bool throwOnNoImplementations = true)
  168. {
  169. var info = GetProperInfoAndCache(desiredType, declaringType);
  170. if (info.resultType is null)
  171. {
  172. if (throwOnNoImplementations)
  173. throw new ArgumentException($"type {desiredType} doesn't has an implement");
  174. return null;
  175. }
  176. if (info.scopeMode == Single)
  177. {
  178. if (!singletons.TryGetValue(info.resultType, out var inst))
  179. {
  180. singletons[info.resultType] = inst = CreateInstance(info);
  181. }
  182. return inst;
  183. }
  184. else
  185. {
  186. return CreateInstance(info);
  187. }
  188. }
  189. public static T Get<T>(Type declaringType = null)
  190. {
  191. var inst = Get(typeof(T), declaringType);
  192. return (T)inst;
  193. }
  194. public static ServiceScopeMode GetScopeMode<TContract>(Type declaringType = null)
  195. {
  196. var info = GetInfoWithCache(typeof(TContract), declaringType);
  197. return info.scopeMode;
  198. }
  199. /// <summary>
  200. /// for members declared in type:<typeparamref name="T"/> ...
  201. /// </summary>
  202. /// <typeparam name="T"></typeparam>
  203. /// <returns></returns>
  204. public static BindDeclaringContext In<T>()
  205. {
  206. return In(typeof(T));
  207. }
  208. /// <summary>
  209. /// for members declared in <paramref name="targetType"/> ...
  210. /// </summary>
  211. /// <param name="targetType"></param>
  212. /// <returns></returns>
  213. public static BindDeclaringContext In(Type targetType)
  214. {
  215. return new BindDeclaringContext(targetType);
  216. }
  217. /// <summary>
  218. /// for any members with type:<typeparamref name="T"/> ...
  219. /// </summary>
  220. /// <typeparam name="T"></typeparam>
  221. /// <returns></returns>
  222. public static BindSourceContext<T> Bind<T>()
  223. {
  224. return new BindDeclaringContext(null).Bind<T>(); // any declaring type
  225. }
  226. }
  227. public struct BindDeclaringContext
  228. {
  229. Type declaringType;
  230. internal BindDeclaringContext(Type declaringType)
  231. {
  232. this.declaringType = declaringType;
  233. }
  234. /// <summary>
  235. /// for members with type <typeparamref name="TSource"/>...
  236. /// </summary>
  237. /// <typeparam name="TSource"></typeparam>
  238. /// <returns></returns>
  239. public BindSourceContext<TSource> Bind<TSource>()
  240. {
  241. return new BindSourceContext<TSource>(declaringType);
  242. }
  243. }
  244. public struct BindSourceContext<TSource>
  245. {
  246. Type declaringType;
  247. Type desiredType;
  248. bool noLazy;
  249. ServiceScopeMode scopeMode;
  250. internal BindSourceContext(Type declaringType)
  251. {
  252. this.declaringType = declaringType;
  253. this.desiredType = typeof(TSource);
  254. this.scopeMode = ServiceScopeMode.Single;
  255. this.noLazy = false;
  256. }
  257. /// <summary>
  258. /// returns a new instance when get
  259. /// </summary>
  260. /// <returns></returns>
  261. public BindSourceContext<TSource> AsTransient()
  262. {
  263. this.scopeMode = ServiceScopeMode.Transient;
  264. return this;
  265. }
  266. /// <summary>
  267. /// return the singleton when get
  268. /// </summary>
  269. /// <param name="noLazy">instantiate immediately</param>
  270. /// <returns></returns>
  271. public BindSourceContext<TSource> AsSingle(bool noLazy = false)
  272. {
  273. this.scopeMode = ServiceScopeMode.Single;
  274. this.noLazy = noLazy;
  275. return this;
  276. }
  277. /// <summary>
  278. /// bind members with type <typeparamref name="TSource"/> to type <typeparamref name="TDest"/>
  279. /// </summary>
  280. /// <param name="arguments"></param>
  281. /// <typeparam name="TDest"></typeparam>
  282. public void To<TDest>(params object[] arguments) where TDest : TSource
  283. {
  284. var typePair = (desiredType, declaringType);
  285. ServiceContainer.lutInfos[typePair] = new()
  286. {
  287. resultType = typeof(TDest),
  288. scopeMode = scopeMode,
  289. constructorArguments = arguments,
  290. creator = null,
  291. };
  292. if (noLazy && scopeMode == ServiceScopeMode.Single)
  293. {
  294. ServiceContainer.Get(desiredType, declaringType);
  295. }
  296. }
  297. public void To<TDest>(Func<TDest> creator) where TDest : TSource
  298. {
  299. var typePair = (desiredType, declaringType);
  300. DebugHelper.Log($"to {scopeMode} {typeof(TDest)}");
  301. ServiceContainer.lutInfos[typePair] = new()
  302. {
  303. resultType = typeof(TDest),
  304. scopeMode = scopeMode,
  305. // constructorArguments = arguments,
  306. creator = () => creator()
  307. };
  308. if (noLazy && scopeMode == ServiceScopeMode.Single)
  309. {
  310. ServiceContainer.Get(desiredType, declaringType);
  311. }
  312. }
  313. }
  314. struct Info
  315. {
  316. public Type resultType;
  317. public ServiceScopeMode scopeMode;
  318. public object[] constructorArguments;
  319. public Func<object> creator;
  320. }
  321. }