Skip to content

Commit

Permalink
Merge pull request #11 from imaxs/develop
Browse files Browse the repository at this point in the history
Version 1.0.2
  • Loading branch information
imaxs authored Feb 12, 2023
2 parents d94e66a + 14a258f commit ab5661a
Show file tree
Hide file tree
Showing 71 changed files with 2,247 additions and 196 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions Framework/Binding/Implemantions/BindingFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,13 @@ public IBindingCondition AddBinding(object value, bool UseDefaultConstructor)
return this.CreateBindingConditionFactoryProvider(binding);
}

/// <inheritdoc cref="IBindingFactory.AddFactoryInstance(Type, IFactory)"/>
public IBindingInjection AddFactoryInstance(Type type, IFactory factory)
/// <inheritdoc cref="IBindingFactory.AddFactoryInstance(Type, IFactory, object)"/>
public IBindingInjection AddFactoryInstance(Type type, IFactory factoryInstance, object value = null)
{
var binding = new BindingData(this.BindingType, factory, type, BindingInstanceType.Factory | BindingInstanceType.Instance);
var binding = new BindingData(this.BindingType,
factoryInstance,
value,
BindingInstanceType.Factory | BindingInstanceType.Instance);
this.Binder.AddBinding(binding);

return this.CreateBindingInjectionFactoryProvider(binding);
Expand Down
11 changes: 6 additions & 5 deletions Framework/Binding/Interfaces/IBindingFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace EasyJection.Binding
{
using EasyJection.Types;
using Hooking;

#region Comment
/// <summary>
/// Contains the definitions of the binding factory.
Expand Down Expand Up @@ -57,11 +57,11 @@ namespace EasyJection.Binding
/// <description>Binds the key type to a <paramref name="type"/> as a singleton.</description>
/// </item>
/// <item>
/// <term><see cref="IBindingFactory.To(Type)"/></term>
/// <term><see cref="IBindingFactory.To(Type, bool)"/></term>
/// <description>Binds the key type to a <paramref name="type"/> as a transient.</description>
/// </item>
/// <item>
/// <term><see cref="IBindingFactory.To{T}()"/></term>
/// <term><see cref="IBindingFactory.To{T}(bool)"/></term>
/// <description>Binds the key type to a type of <typeparamref name="T"/> as a transient.</description>
/// </item>
/// <item>
Expand All @@ -81,7 +81,7 @@ namespace EasyJection.Binding
/// <description>Binds the key type to an <paramref name="instance"/> of a type which has implemented <see cref="Types.IFactory"/> interface.</description>
/// </item>
/// <item>
/// <term><see cref="IBindingFactory.AddBinding(object)"/></term>
/// <term><see cref="IBindingFactory.AddBinding(object, bool)"/></term>
/// <description>Creates a binding.</description>
/// </item>
/// <item>
Expand Down Expand Up @@ -233,7 +233,8 @@ public interface IBindingFactory
/// </summary>
/// <param name="type">The type of instance that will be created by the <see cref="Types.IFactory"/> factory.</param>
/// <param name="factoryInstance">Factory creating instances</param>
/// <param name="value">[Optional] Some value that can be used by the factory during instantiation.</param>
/// <returns>The binding injection object related to this binding.</returns>
IBindingInjection AddFactoryInstance(Type type, IFactory factoryInstance);
IBindingInjection AddFactoryInstance(Type type, IFactory factoryInstance, object value = null);
}
}
4 changes: 4 additions & 0 deletions Framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.2] - 2023-02-13
- Fixed bugs with dependency resolution for instances created by the factory.
- Added support resolve for an array in field.

## [1.0.1] - 2023-02-11
- Fixed a bug with injection to a default constructor.
- Added the ability to bind a factory class without an inherited 'EasyJection.Types.IFactory' and specify the method for creating instances by method name.
Expand Down
50 changes: 42 additions & 8 deletions Framework/Resolving/Implementions/Resolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ public object Resolve(Type type, IDictionary<Type, object> scopedInstances)
if (bindingData.InstanceType.HasFlag(BindingInstanceType.Instance))
{
if (bindingData.InstanceType.HasFlag(BindingInstanceType.Factory))
return bindingData.Factory.CreateInstance(bindingData);
{
var instValue = bindingData.Factory.CreateInstance(bindingData);
this.Inject(instValue);
return instValue;
}
else
return bindingData.Value;
}
Expand All @@ -132,6 +136,11 @@ public object Resolve(Type type, IDictionary<Type, object> scopedInstances)
return instance;
}

protected object Resolve(Type type)
{
return this.Resolve(type, null);
}

/// <inheritdoc cref="IResolver.Resolve(object[], Type[])"/>
public object[] Resolve(object[] objects, Type[] types, IDictionary<Type, object> scopedInstances)
{
Expand Down Expand Up @@ -196,11 +205,17 @@ protected object Instantiate(Type instanceType, IBindingData bindingData, IDicti
hookManager.Hook();
}

// Add an item to the scoped dictionary
scopedInstances.Add(bindingData.Type, instance);

// Dependency Injection
this.Inject(instance, this.cache[instanceType], scopedInstances);
if (scopedInstances != null)
{
// Add an item to the scoped dictionary
scopedInstances.Add(bindingData.Type, instance);
// Dependency Injection
this.Inject(instance, this.cache[instanceType], scopedInstances);
}
else
{
this.Inject(instance);
}

return instance;
}
Expand Down Expand Up @@ -232,15 +247,15 @@ protected void InjectFields(object instance, AccessoriesInfo[] fields, IDictiona
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
{
var field = fields[fieldIndex];

var fieldType = field.Type;
var value = field.InvokeGetter(instance);

// The Equals(null) comparison is used to ensure that null is evaluated correctly due to the null trick
if (value == null || value.Equals(null))
{
try
{
var valueToSet = scopedInstances.ContainsKey(field.Type) ? scopedInstances[field.Type] : this.Resolve(field.Type, scopedInstances);
var valueToSet = scopedInstances.ContainsKey(fieldType) ? scopedInstances[fieldType] : this.Resolve(fieldType, scopedInstances);
field.InvokeSetter(instance, valueToSet);
}
catch (Exception exception)
Expand All @@ -249,6 +264,25 @@ protected void InjectFields(object instance, AccessoriesInfo[] fields, IDictiona
string.Format(Causes.UNABLE_TO_INJECT_ON_FIELD, field.Name, instance.GetType(), exception.Message), exception);
}
}
else if (fieldType.IsArray)
{
try
{
var elemType = fieldType.GetElementType();
var array = (field.InvokeGetter(instance) as object[]);
for (int i = 0; i < array.Length; i++)
{
if (array[i] == null)
array[i] = this.Resolve(elemType);
}
scopedInstances.Add(fieldType, array);
}
catch (Exception exception)
{
throw new Exception(
string.Format(Causes.UNABLE_TO_INJECT_ON_FIELD, field.Name, instance.GetType(), exception.Message), exception);
}
}
}
}

Expand Down
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
* [To Factory](#-to-factory)
* [To Instance](#-to-instance)
* [To Self](#-to-self)
* [To GameObject](#-to-gameobject)
* [Injection Conditions](#injection-conditions)
* [Constructor Injection](#-constructor-injection)
* [Method Injection](#-method-injection)
* [Non-return Method (MethodVoid)](#non-return-method-methodvoid)
* [Method with result (MethodResult)](#method-with-result-methodresult)
* [Passing Arguments](#passing-arguments)
* [Array Injection](#array-injection)
* [Injection Notes](#injection-notes)
* [Change Log](#-change-log)
* [Contributing](#-contributing)
Expand Down Expand Up @@ -561,7 +563,24 @@ container.Bind<SomeClass>().ToSelf(UseDefaultConstructor: True | False);
```
Where:
- *UseDefaultConstructor* — If True, the injection occurs each time the default constructor is called (from `new()`).


#### 🔘 To GameObject ####
The EasyJection framework allows you to create a binding to a Unity's gameobject that has a type of component you need.

To bind to a gameobject, it must first be added to the installer's gameobjects collection:

<img src="https://github.com/imaxs/EasyJection/blob/develop/Documentation/Images/InstallerGameObjectsCollection.png?sanitize=true)"/>
The added prefab named `Cube` has a `Cube` component implementing a `ICube` interface and inherited from MonoBehaviour.

After that the specified prefab named `Cube` can be used for binding as transient.

```csharp
// "Cube" is the key name of the gameobject in the installer collection
Container.Bind<ICube>().ToGameObject<Cube>("Cube");
// or so, in this case the type name is used (⚠️ a type name should match a key in the collection)
Container.Bind<ICube>().ToGameObject<Cube>();
```

### Injection Conditions ###
EasyJection provides injection through a constructor or method call. Constructor injection forces the dependency to only be resolved once, at instance creation, which is usually what you want. Inject methods are the recommended approach for MonoBehaviours (e.g. 'Awake' and 'Start' methods). Injection conditions are set by calling the `InjectionTo()` method. In order to specify a constructor or method for injection, you need to specify its signature.

Expand Down Expand Up @@ -596,7 +615,6 @@ container.Bind<SomeClass>()
.Constructor<T1, T2 ... T9>(UseForInstantiation: True | False)
.WithArguments<T1, T2 ... T9>(T1 arg1, T2 arg2 ... T9 arg9);
```

Where:
- *UseForInstantiation* — if True, the container will use this constructor to create an instance, otherwise it will use the default constructor.
- *<T1, T2 ... T9>* — types of constructor parameters.
Expand Down Expand Up @@ -628,6 +646,7 @@ container.Bind<SomeClass>()
.MethodVoid<T1, T2 ... T9>(methodName);
.WithArguments<T1, T2 ... T9>(T1 arg1, T2 arg2 ... T9 arg9);
```
Where:
- *methodName* — the name of a non-return method
- *<T1, T2 ... T9>* — types of constructor parameters.
##### Method with result (MethodResult) #####
Expand Down Expand Up @@ -655,6 +674,7 @@ container.Bind<SomeClass>()
.MethodResult<T1, T2 ... T9, TResult>(methodName);
.WithArguments<T1, T2 ... T9>(T1 arg1, T2 arg2 ... T9 arg9);
```
Where:
- *methodName* — the name of a method.
- *<T1, T2 ... T9>* — types of constructor parameters.
- *TResult* — type of return value.
Expand Down Expand Up @@ -700,8 +720,43 @@ Result:
Where:
- *UseForInstantiation* — if True, the container will use this constructor to create an instance, otherwise it will use the default constructor.

#### Array Injection ####
The EasyJection framework can inject and resolve all registered implementations for each array element in a field.

```csharp
public class Foo
{
public ISomeInterface[] fieldArray;

[MethodImpl(MethodImplOptions.NoInlining)]
// The array must be created before injection.
public SomeClass()
// the size of the array is 10
: this(new ISomeInterface[10])
{ }

[MethodImpl(MethodImplOptions.NoInlining)]
private SomeClass(ISomeInterface[] array)
{
this.fieldArray = array;
}
}

...
// Binding
var container = new Container();
container.Bind<ISomeInterface>().To<SomeClass>();
container.Bind<Foo>().ToSelf(UseDefaultConstructor: true);

...
// So now the EasyJection framework creates and resolves 10 elements in a field named 'fieldArray'
var instance = new Foo();
```

#### Injection Notes ####

// TODO

## 💾 Change Log ##

All notable changes to this project will be documented in files:
Expand Down
6 changes: 6 additions & 0 deletions UnityPackage/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.2] - 2023-02-13
- Fixed bugs with dependency resolution for instances created by the factory.
- Added support resolve for an array in field.
- Added support for binding to GameObject.
- Updated samples and added new ones.

## [1.0.1] - 2023-02-11
- Fixed a bug with injection to a default constructor.
- Added the ability to bind a factory class without an inherited 'EasyJection.Types.IFactory' and specify the method for creating instances by method name.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of the EasyJection Framework.
* Author: Max Karepin (http://github.com/imaxs/)
*
* Copyright © 2022 Max Karepin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WII || UNITY_IOS || UNITY_ANDROID || UNITY_PS4 || UNITY_XBOXONE || UNITY_TIZEN || UNITY_TVOS || UNITY_WSA || UNITY_WEBGL || UNITY_FACEBOOK
#define UNITY_ENGINE_AVAILABLE
#endif

namespace EasyJection.Extensions
{
#if UNITY_ENGINE_AVAILABLE

using UnityEngine;
/// <summary>
/// Represents a prefab's binding.
/// </summary>
public class PrefabBinding
{
public string KeyName { get; private set; }
/// <summary>
/// The prefab to instantiate.
/// </summary>
public Object Prefab { get; set; }

/// <summary>
/// The type that will be resolved from the prefab.
/// </summary>
public System.Type Type { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="PrefabBinding"/> class.
/// </summary>
/// <param name="prefab">The prefab to be instantiated.</param>
/// <param name="type">The type that will be resolved from the prefab.</param>
public PrefabBinding(string key, Object prefab, System.Type type)
{
this.KeyName = key;
this.Prefab = prefab;
this.Type = type;
}

public PrefabBinding(string key, System.Type type)
: this(key, null, type)
{ }
}
#endif
}

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

Loading

0 comments on commit ab5661a

Please sign in to comment.