-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
191b012
commit dec731f
Showing
6 changed files
with
316 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using UnityEngine; | ||
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; | ||
static Dictionary<Type, object> singletons = new(); | ||
static Dictionary<Type, Info> lutInfos = new(); | ||
public static void ClearInstances(){ | ||
singletons.Clear(); | ||
} | ||
public static object Get(Type type) | ||
{ | ||
if (!lutInfos.TryGetValue(type, out var info)) | ||
{ | ||
var subtypes = Retriever.GetAllSubtypes(type) | ||
.Where(t => !t.IsInterface) | ||
.Where(t => !t.IsAbstract) | ||
.ToArray() | ||
; | ||
if (subtypes.Length == 0) | ||
{ | ||
throw new ArgumentException($"type {type} doesn't has an implement"); | ||
} | ||
if (subtypes.Length > 1) | ||
{ | ||
Debug.LogWarning($"type {type} exists more than one implements"); | ||
} | ||
lutInfos[type] = info = new Info() | ||
{ | ||
resultType = subtypes[0], | ||
scopeMode = DefaultScopeMode, | ||
}; | ||
} | ||
if (info.scopeMode == Single && singletons.TryGetValue(type, out var existing)) | ||
{ | ||
return existing; | ||
} | ||
var inst = Activator.CreateInstance(info.resultType); | ||
if (info.scopeMode == Single && inst != null) | ||
{ | ||
singletons[type] = inst; | ||
} | ||
return inst; | ||
} | ||
|
||
public static T Get<T>() | ||
{ | ||
return (T)Get(typeof(T)); | ||
} | ||
|
||
public static void AddTransient<TContract, TResult>() | ||
{ | ||
lutInfos[typeof(TContract)] = new() | ||
{ | ||
resultType = typeof(TResult), | ||
scopeMode = Transient, | ||
}; | ||
} | ||
|
||
public static void AddSingle<TContract, TResult>(bool noLazy = false) | ||
{ | ||
lutInfos[typeof(TContract)] = new() | ||
{ | ||
resultType = typeof(TResult), | ||
scopeMode = Single, | ||
}; | ||
if(noLazy) Get<TContract>(); | ||
} | ||
|
||
public static ServiceScopeMode GetScopeMode<TContract>() | ||
{ | ||
if (lutInfos.TryGetValue(typeof(TContract), out var info)) | ||
{ | ||
return info.scopeMode; | ||
} | ||
return DefaultScopeMode; | ||
} | ||
|
||
} | ||
struct Info | ||
{ | ||
public Type resultType; | ||
public ServiceScopeMode scopeMode; | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
Runtime/ClassicalUsages/SimpleDI.cs.meta → .../ClassicalUsages/ServiceContainer.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using com.bbbirder.injection; | ||
using UnityEngine.Assertions; | ||
using UnityEngine.Scripting; | ||
|
||
|
||
namespace com.bbbirder.injection | ||
{ | ||
using static System.Reflection.BindingFlags; | ||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] | ||
public class SimpleDIAttribute : InjectionAttribute | ||
{ | ||
static MethodInfo s_MetaMethodInfo; | ||
static MethodInfo s_StaticMetaMethodInfo; | ||
static MethodInfo s_miMetaConstructor; | ||
public SimpleDIAttribute() | ||
{ | ||
} | ||
|
||
bool IsStatic(MemberInfo memberInfo) | ||
{ | ||
if (memberInfo is FieldInfo fieldInfo) return fieldInfo.IsStatic; | ||
if (memberInfo is PropertyInfo propertyInfo) | ||
{ | ||
if (!propertyInfo.CanRead) | ||
{ | ||
return propertyInfo.GetSetMethod(nonPublic: true).IsStatic; | ||
} | ||
return propertyInfo.GetGetMethod(nonPublic: true).IsStatic; | ||
} | ||
return false; | ||
} | ||
|
||
[Preserve] | ||
static TFunc MetaConstructor<TFunc>(Action<object> action, Type instType, bool isStatic) | ||
where TFunc : Delegate | ||
{ | ||
var argtypes = typeof(TFunc).GetGenericArguments(); | ||
var act = Expression.Constant(action); | ||
var args = new List<ParameterExpression>(argtypes.Length + 1); | ||
// if (!isStatic) args.Add(Expression.Parameter(instType, "inst")); | ||
for (int i = 0; i < argtypes.Length; i++) | ||
{ | ||
args.Add(Expression.Parameter(argtypes[i], "arg" + i)); | ||
} | ||
var ivk = Expression.Call(act, | ||
typeof(Action<object>).GetMethod("Invoke"), | ||
isStatic ? Expression.Constant(null) : args[0]); | ||
var lambda = Expression.Lambda<TFunc>(ivk, args); | ||
return lambda.Compile(); | ||
} | ||
|
||
ConstructorInfo[] Get_Ctors(Type type) | ||
{ | ||
return type.GetConstructors(Public | NonPublic | Instance); | ||
} | ||
|
||
ConstructorInfo[] Get_CCtor(Type type) | ||
{ | ||
return type.GetConstructors(Public | NonPublic | Static); | ||
} | ||
|
||
bool CanWrite(MemberInfo memberInfo) | ||
{ | ||
if (memberInfo is FieldInfo) return true; | ||
if (memberInfo is PropertyInfo propertyInfo) return propertyInfo.CanWrite; | ||
return false; | ||
} | ||
void SetMemberValue(MemberInfo memberInfo, object inst, object value) | ||
{ | ||
if (memberInfo is FieldInfo fi) | ||
{ | ||
fi.SetValue(inst, value); | ||
return; | ||
} | ||
if (memberInfo is PropertyInfo pi) | ||
{ | ||
pi.SetValue(inst, value); | ||
return; | ||
} | ||
} | ||
Type GetMemberType(MemberInfo memberInfo) | ||
{ | ||
if (memberInfo is FieldInfo fi) return fi.FieldType; | ||
if (memberInfo is PropertyInfo pi) return pi.PropertyType; | ||
return default; | ||
} | ||
public override IEnumerable<InjectionInfo> ProvideInjections() | ||
{ | ||
s_MetaMethodInfo ??= typeof(SimpleDIAttribute).GetMethod(nameof(MetaGet), Static | NonPublic); | ||
s_StaticMetaMethodInfo ??= typeof(SimpleDIAttribute).GetMethod(nameof(StaticMetaGet), Static | NonPublic); | ||
s_miMetaConstructor ??= typeof(SimpleDIAttribute).GetMethod(nameof(MetaConstructor), Static | NonPublic); | ||
if (targetMember is not PropertyInfo and not FieldInfo) | ||
throw new Exception($"cannot inject {targetMember} on type {targetType}, only fields and properties allowed"); | ||
var memberType = GetMemberType(targetMember); | ||
var isStatic = IsStatic(targetMember); | ||
var canWrite = CanWrite(targetMember); | ||
if (!Retriever.IsTypeRetrievable(memberType)) | ||
{ | ||
throw new Exception($"cannot inject {targetMember} on type {targetType}, type {memberType} is not retrievable"); | ||
} | ||
if (isStatic) | ||
{ | ||
if (canWrite) | ||
{ | ||
// set on fix instantly | ||
yield return InjectionInfo.Create(()=>{ | ||
SetMemberValue(targetMember, null, GetContainerInst(memberType)); | ||
}); | ||
} | ||
else | ||
{ | ||
// inject get method | ||
var propertyInfo = targetMember as PropertyInfo; | ||
var fixingMethod = s_StaticMetaMethodInfo.MakeGenericMethod(propertyInfo.PropertyType); | ||
yield return InjectionInfo.Create( | ||
propertyInfo.GetGetMethod(nonPublic: true), | ||
fixingMethod | ||
); | ||
} | ||
} | ||
else | ||
{ | ||
if (canWrite) | ||
{ | ||
// inject constructor | ||
var constructors = Get_Ctors(targetType); | ||
var argtypes = new List<Type>(); | ||
foreach (var constructor in constructors) | ||
{ | ||
Delegate rawAction = default; | ||
argtypes.Clear(); | ||
argtypes.Add(targetType); | ||
foreach (var p in constructor.GetParameters()) | ||
{ | ||
argtypes.Add(p.ParameterType); | ||
} | ||
var miInstAction = default(Type); | ||
if (argtypes.Count == 0) | ||
{ | ||
miInstAction = typeof(System.Action); | ||
} | ||
else | ||
{ | ||
var miGenericAction = Type.GetType("System.Action`" + argtypes.Count); | ||
miInstAction = miGenericAction.MakeGenericType(argtypes.ToArray()); | ||
} | ||
var miCtorInst = s_miMetaConstructor.MakeGenericMethod(miInstAction); | ||
var fixingFunc = miCtorInst.Invoke(null, new object[]{ | ||
(Action<object>)fixedContructor,targetType,isStatic | ||
}) as Delegate; | ||
yield return InjectionInfo.Create( | ||
constructor, | ||
fixingFunc, | ||
f => rawAction = f | ||
); | ||
void fixedContructor(object inst) | ||
{ | ||
SetMemberValue(targetMember, inst, GetContainerInst(memberType)); | ||
rawAction.GetType().GetMethod("Invoke").Invoke(rawAction, new[] { inst }); | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
// inject get method | ||
var propertyInfo = targetMember as PropertyInfo; | ||
var fixingMethod = s_MetaMethodInfo.MakeGenericMethod(targetType, propertyInfo.PropertyType); | ||
yield return InjectionInfo.Create( | ||
propertyInfo.GetGetMethod(nonPublic: true), | ||
fixingMethod | ||
); | ||
} | ||
} | ||
} | ||
|
||
static TRet MetaGet<T, TRet>(T _) where T : class where TRet : class | ||
{ | ||
return GetContainerInst(typeof(TRet)) as TRet; | ||
} | ||
|
||
static TRet StaticMetaGet<TRet>() where TRet : class | ||
{ | ||
return GetContainerInst(typeof(TRet)) as TRet; | ||
} | ||
|
||
static object GetContainerInst(Type type) | ||
{ | ||
return ServiceContainer.Get(type); | ||
} | ||
} | ||
} |
Oops, something went wrong.