Skip to content

Commit

Permalink
Merge pull request #42 from mewlist/field-injection
Browse files Browse the repository at this point in the history
Field Injection
  • Loading branch information
mewlist authored Feb 21, 2024
2 parents b148ec7 + dd5143f commit d64cabe
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 5 deletions.
50 changes: 46 additions & 4 deletions Runtime/DIContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class DIContainer : IReadOnlyDIContainer, IAsyncDisposable
{
private static Dictionary<Type, TargetMethodsInfo> MethodInfoMap { get; } = new();
private static Dictionary<Type, TargetPropertiesInfo> PropertyInfoMap { get; } = new();
private static Dictionary<Type, TargetFieldsInfo> FieldInfoMap { get; } = new();

private IReadOnlyDIContainer Parent { get; set; }
private Scene Scene { get; set; }
Expand All @@ -35,6 +36,7 @@ public class DIContainer : IReadOnlyDIContainer, IAsyncDisposable
private ConstructorInjector ConstructorInjector { get; }
private MethodInjector MethodInjector { get; }
private PropertyInjector PropertyInjector { get; }
private FieldInjector FieldInjector { get; }

public IReadOnlyDictionary<TargetTypeInfo, IInternalResolver> ReadOnlyBindings => Resolvers;
internal IReadOnlyDictionary<TargetTypeInfo, ConcurrentObjectBag> ReadOnlyInstanceMap => ResolvedInstanceBag.ReadOnlyInstanceMap;
Expand All @@ -51,6 +53,7 @@ public DIContainer(IReadOnlyDIContainer parent = null, Scene scene = default)
ConstructorInjector = new ConstructorInjector(this);
MethodInjector = new MethodInjector(this);
PropertyInjector = new PropertyInjector(this);
FieldInjector = new FieldInjector(this);
BindFromInstance<IReadOnlyDIContainer, DIContainer>(this);
}

Expand Down Expand Up @@ -183,13 +186,28 @@ private async ValueTask<object> InstantiateInternalAsync(Type targetType, object
AfterInjectionProcessingScope.Begin();

var target = await ConstructorInjector.DoInject(targetType, args, scopedInstances);
var targetMethodsInfo = GetTargetMethodInfo(targetType);
var targetPropertiesInfo = GetTargetPropertyInfo(targetType);

TargetMethodsInfo targetMethodsInfo;
TargetPropertiesInfo targetPropertiesInfo;
TargetFieldsInfo targetFieldsInfo;
try
{
targetMethodsInfo = GetTargetMethodInfo(targetType);
targetPropertiesInfo = GetTargetPropertyInfo(targetType);
targetFieldsInfo = GetTargetFieldInfo(targetType);
}
catch (Exception e)
{
InjectionProcessingScope.End();
AfterInjectionProcessingScope.End();
throw new FailedToInjectException(target, e);
}

try
{
await MethodInjector.DoInject(target, targetMethodsInfo, args, scopedInstances);
await PropertyInjector.DoInject(target, targetPropertiesInfo);
await FieldInjector.DoInject(target, targetFieldsInfo);
}
catch (Exception e)
{
Expand Down Expand Up @@ -270,13 +288,27 @@ private async ValueTask InjectIntoInternalAsync<T>(T target, object[] args, Scop
AfterInjectionProcessingScope.Begin();

var targetType = target.GetType();
var targetMethodsInfo = GetTargetMethodInfo(targetType);
var targetPropertiesInfo = GetTargetPropertyInfo(targetType);
TargetMethodsInfo targetMethodsInfo;
TargetPropertiesInfo targetPropertiesInfo;
TargetFieldsInfo targetFieldsInfo;
try
{
targetMethodsInfo = GetTargetMethodInfo(targetType);
targetPropertiesInfo = GetTargetPropertyInfo(targetType);
targetFieldsInfo = GetTargetFieldInfo(targetType);
}
catch (Exception e)
{
InjectionProcessingScope.End();
AfterInjectionProcessingScope.End();
throw new FailedToInjectException(target, e);
}

try
{
await MethodInjector.DoInject(target, targetMethodsInfo, args, scopedInstances);
await PropertyInjector.DoInject(target, targetPropertiesInfo);
await FieldInjector.DoInject(target, targetFieldsInfo);
}
catch (Exception e)
{
Expand Down Expand Up @@ -414,6 +446,16 @@ private TargetPropertiesInfo GetTargetPropertyInfo(Type targetType)
return targetPropertiesInfo;
}

private TargetFieldsInfo GetTargetFieldInfo(Type targetType)
{
if (FieldInfoMap.TryGetValue(targetType, out var targetFieldsInfo))
return targetFieldsInfo;

targetFieldsInfo = new TargetFieldsInfo(targetType);
FieldInfoMap[targetType] = targetFieldsInfo;
return targetFieldsInfo;
}

public async ValueTask DisposeAsync()
{
if (CancellationTokenSource.IsCancellationRequested) return;
Expand Down
36 changes: 36 additions & 0 deletions Runtime/Injector/FieldInjector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading.Tasks;

namespace Doinject
{
internal class FieldInjector
{
private DIContainer Container { get; }
private ParameterBuilder ParameterBuilder { get; }

public FieldInjector(DIContainer container)
{
Container = container;
ParameterBuilder = container.ParameterBuilder;
}

public async ValueTask DoInject<T>(
T target,
TargetFieldsInfo fields)
{
if (!fields.Any()) return;
var targetType = target.GetType();
var resolverType = targetType;

if (targetType == typeof(IInjectableComponent))
resolverType = target.GetType();

Container.MarkInjected(resolverType);

foreach (var fieldInfo in fields.InjectFields)
{
var instance = await Container.ResolveAsync(fieldInfo.FieldType);
fieldInfo.SetValue(target, instance);
}
}
}
}
3 changes: 3 additions & 0 deletions Runtime/Injector/FieldInjector.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions Runtime/TargetFieldsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Doinject
{
internal class TargetFieldsInfo
{
public List<FieldInfo> InjectFields { get; } = new();

public TargetFieldsInfo(Type targetType)
{
foreach (var fieldInfo in targetType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (fieldInfo.GetCustomAttributes(typeof(InjectAttribute), true).Length <= 0)
continue;

if (!fieldInfo.IsPublic)
throw new Exception($"Field must be public. {targetType.Name}.{fieldInfo.Name}");
InjectFields.Add(fieldInfo);
}
}

public bool Any()
=> InjectFields.Count > 0;
}
}
3 changes: 3 additions & 0 deletions Runtime/TargetFieldsInfo.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions Tests/Components/FieldInjectionComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using UnityEngine;

namespace Doinject.Tests
{
public class FieldInjectionComponent : MonoBehaviour, IInjectableComponent
{
[Inject] public InjectedObject injectedObject;
}
}
3 changes: 3 additions & 0 deletions Tests/Components/FieldInjectionComponent.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 34 additions & 1 deletion Tests/InjectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,43 @@ public async Task PropertyInjectionWithNonPublicSetterComponentTest()
{
await container.ResolveAsync<PropertyInjectionWithNonPublicSetterComponent>();
}
catch (Exception e)
catch (Exception _)
{
Assert.Pass();
}
Assert.Fail();
}

[Test]
public async Task FieldInjectionTest()
{
container.BindTransient<FieldInjectionObject>();
container.BindTransient<InjectedObject>();
var instance = await container.ResolveAsync<FieldInjectionObject>();
Assert.NotNull(instance.injectedObject);
}

[Test]
public async Task FieldInjectionComponentTest()
{
container.BindTransient<FieldInjectionComponent>();
container.BindTransient<InjectedObject>();
var instance = await container.ResolveAsync<FieldInjectionComponent>();
Assert.NotNull(instance.injectedObject);
}

[Test]
public async Task FieldInjectionWithNonPublicSetterComponentTest()
{
container.BindTransient<FieldInjectionWithNonPublicObject>();
container.BindTransient<InjectedObject>();
try
{
await container.ResolveAsync<FieldInjectionWithNonPublicObject>();
}
catch (Exception _)
{
Assert.Pass();
}
Assert.Fail();
}
Expand Down
10 changes: 10 additions & 0 deletions Tests/TestObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ public class PropertyInjectionObject
[Inject] public InjectedObject InjectedObject { get; set; }
}

public class FieldInjectionObject
{
[Inject] public InjectedObject injectedObject;
}

public class FieldInjectionWithNonPublicObject
{
[Inject] private InjectedObject injectedObject;
}

public class InjectableObject : IDisposable
{
public InjectedObject InjectedObject { get; set; }
Expand Down

0 comments on commit d64cabe

Please sign in to comment.