Giter Club home page Giter Club logo

ssannandeji / zenject-2019 Goto Github PK

View Code? Open in Web Editor NEW

This project forked from sebas77/-obsolete-lightweight-ioc-container-for-unity3d

2.5K 160.0 366.0 37.33 MB

Dependency Injection Framework for Unity3D

License: MIT License

C# 97.02% Batchfile 0.04% Python 2.77% ShaderLab 0.17%
dependency-injection unity3d injection-container dependency-injection-framework game game-development unity3d-plugin gamedev zenject unity framework dependency injection ioc data-binding

zenject-2019's People

Contributors

bitbutter avatar bvance avatar cooloon avatar foobraco avatar gitter-badger avatar hdanilo avatar jamesdaviesss avatar jamjardavies avatar jdessart-mt avatar jhett12321 avatar johnlcox avatar kruncher avatar marsermd avatar mitchellmarx avatar movestill avatar mwamboldtmt avatar mysticfall avatar pereviader avatar rtaylornc avatar ryannielson avatar shreyasd avatar simeonradivoev avatar soren025 avatar ssannandeji avatar suzdalnitski avatar svermeulen avatar tertle avatar thematthopkins avatar yacuzo avatar zeljkokalezic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zenject-2019's Issues

BindAllInterfacesToInstance not detecting interfaces when type is object

When registering a variable whose declared type is object using DiContainer.BindAllInterfacesToInstance<TConcrete>(TConcrete value), the typeof(TConcrete) statement is evaluating to Object, and not the type of the value argument. This results in the interfaces of value not being found.

Calling BindAllInterfacesToInstance(myInstance.GetType(), myInstance) does work however.

Possibly DiContainer.BindAllInterfacesToInstance<TConcrete>(TConcrete value) does not need to be generic? This would fix the issue.

Func 6

I have this issue after update from asset store. Removing of Func<T1, T2, T3, T4, T5, TResult> from Func file in Internal folder solves the problem.

[WP8] TypeAnalyzer.GetInjectConstructor strange behavior

GetInjectConstructor retrieves at least 2 constructors whatever the type:

  1. Void .ctor() with Attributes = FamANDAssem | Family | HideBySig | SpecialName | RTSpecialName
  2. Void .ctor(UIntPtr) with Attributes = Private | Family | HideBySig | SpecialName | RTSpecialName

As none is marked with [Inject] GetInjectConstructor always returns NULL
If we bypass test

if (constructors.HasMoreThan(1))
{
  // This will return null if there is more than one constructor and none are marked with the [Inject] attribute
  return (from c in constructors where c.HasAttribute<InjectAttribute>() select c).SingleOrDefault();
}

everything works fine as first one is used as expected.
Anyone an idea regarding this?

DI doesn't trigger

For some reason zenject does neither call the constructor nor the init()method of this Dummy class

public class DummyClass {

        [Inject]
        RoomController controller;

        public DummyClass() {
            Debug.Log("dummy constructor called");
        }

        [PostInject]
        void init() {
            Debug.Log("Dummyclass init called");
        }
    }

This is the binding

public class GameInstaller : MonoInstaller {

        public override void InstallBindings() {
                          Container.Bind<DummyClass>().ToSingle();  
        }
}

Am I doing something wrong here?

Prefabs with [PostInject]

I am creating a small MVC library for Unity. I would like the user to be able to have its prefabs in some folder, tell the framework the path to each prefab and the type of the controller attach to that prefab, so the framework is able to switch between views. Using Zenject I have something like this:

Setup

container.Bind<SomeController>().ToPrefab("path/in/Resources");
app.RegisterRoute<SomeController>("path/in/Resources");

BinderGeneric.ToPrefab extension

public static void ToPrefab<T>(this BinderGeneric<T> binder, String prefabLocation) where T :     UnityEngine.Object {

    binder.ToMethod((ctx) => {
        var obj = UnityEngine.Object.Instantiate(Resources.Load(prefabLocation)) as GameObject;
        return obj.GetComponent<T>();
    });
}

GoTo (String url) method

Type type = views[url];
current = (MonoBehaviour)container.Resolve(type);

It very nice that at least it instantiates the correct object, but the Component returned during the binder.ToMethod lambda is not getting any [PostInject] methods executed. I can solve this two ways but I don't know how:

  1. Is there anything I can do during the binder.ToMethod lamda to get injection into the resulting object;
  2. Is there a way to manually force injection after container.Resolve is called on the GoTo procedure?

Inject and SubContainer

I have code

var c = _container.CreateSubContainer();
c.Bind<int>().ToInstance(x);
c.Resolve<IPoint>();

and I get exeption "ZenjectResolveException: Unable to resolve type 'int' while building object with type 'Point'. "

main container don't get sub-container bindings?

ToSingleInstance dependencies

Are dependencies injected into the object that is created while using ToSingleInstance() kind of injection?

Having

Container.Bind<MyClass>().ToSingleInstance(new MyClass(myparams));

Will MyClass instance get it's field dependencies?

Idea: Add ability to inject into PostInject methods instead of just constructors

In some cases (MonoBehaviours and circular dependencies) we are not able to use constructor injection and therefore must use either property or field injection. What might be nice as another alternative for these cases is to be able to add parameters to the methods that are marked as [PostInject] and have Zenject automatically resolve those dependencies.

TransientMockProvider broken

TransientMockProvider seems to be broken due to Fasterflect missing dependency Method:

public override object GetInstance(Type contractType, InjectContext context)
        {
            object instance;

            if (!_instances.TryGetValue(contractType, out instance))
            {
                instance = typeof(Mock).Method(new[] { contractType }, "Of", Flags.StaticAnyVisibility).Invoke(null, null);
                _instances.Add(contractType, instance);
            }

            return instance;
        }

WP8 System.Array::SetValue(System.Object,System.Int64) doesn't exist

Hi,

just imported ZenJect into my project and when I build my game for WP8 I get the following error:

Error building Player: Exception: Error: method `System.Void System.Array::SetValue(System.Object,System.Int64)` doesn't exist in target framework. It is referenced from Fasterflect.dll at System.Object Fasterflect.ArrayExtensions::SetElement(System.Object,System.Int64,System.Object).
Error: method `System.Object System.Array::GetValue(System.Int64)` doesn't exist in target framework. It is referenced from Fasterflect.dll at System.Object Fasterflect.ArrayExtensions::GetElement(System.Object,System.Int64).

Greetings Christian

More details on how to dynamically create objects

Hi.
Could you please add more information on how to properly instantiate new objects dynamically.
I mean - Instantiating a new Prefab with my object.
GameObject.Instnatiate does not inject dependencies, of course.
I looked into documentation and there seem to be old GameObjectInstantiator, that was removed, and a new IInstantiator interface.

Which implementation of IInstantiator should I use to create a new Prefab in my custom class that is not a MonoInstaller instance?

What I want is to create a new prefab that has some script attached to it, and have all dependencies provided for that script.

Missing git tags

How to know which commit is which release?

Usually, people use tags for that purpose. It would be nice if you could add those tags, as for now there is only 1 tag called "v1.18" while the documentation mentions v3.2 already.

Initial field injection fails on MonoBehaviours attached to an inactive GameObject

In InstallBindings()

    Container.Bind<int>().ToInstance(42);

MonoBehaviour:

    public class Question : MonoBehaviour {

        [Inject]
        protected int Answer = 3;
    }

When that MonoBehaviour is attached to an inactive GameObject and I run the scene in the editor, the field value stays 3. When the GameObject is active, the field value is set to 42.

I'm guessing this is due to the way Unity is reluctant to let you find GameObjects that are inactive.

I would expect the field value to be set to 42 whether or not the GameObject was active.

Injection after LoadLevel

Hello,
I'm trying to use Zenject in a project with multiple scenes and there seems to be no injection for the MonoBehaviours that are loaded from the new scene.

My project baically is:

  • Main scene with context root and bindings for scene independent services.
  • Additional scenes are loaded with ZenUtil.LoadSceneAdditive or ZenUtil.LoadScene + DontDestroyOnLoad on CompositionRoot

I tried to setup the bindings in the main installer and in the delegate for LoadLevel. But nothing gets injected.

Is there any way to get this working, or do I have to change my project structure?

No "ToTransientMethod" method in BinderUntyped

Currently there is only a ToSingleMethod but I specifically need the dependency to be transient. Can this method be added to Zenject? I am trying to do the same as yesterday but now by passing types as parameters

        public static void ToPrefab(this BinderUntyped binder, String prefabLocation, Type type)
        {
            //NEED TRANSIENT VERSION
            binder.ToSingleMethod((ctx) => {
                var obj = Resources.Load(prefabLocation) as GameObject;
                return ctx.Container.InstantiatePrefabForComponent(type, obj) as MonoBehaviour;
            });
        }

Windows Universal support

Currently, I can see Zenject only support Windows 8.0 and Windows Phone 8.0 SDK.

Have you ever planned to support Universal Apps for Windows ? Indeed, there is some bugs when it compiles a build (Tuple namespace, not existing assembly).

Thanks.

No right click menu in Hierarchy-Tab under Unity 5.3.1

Hey,

I downloaded the package from the asset store and I am trying to follow the steps in the Hello-World-Tutorial.

However: The right click menu entry for Zenject is not showing. What am I doing wrong?
I found some entries in the Component Menu, so I will try to work around this somehow.

Just thought I should report this.

Thanks, Ruben

Importing files from the Unity3D package

When importing the package from Unity3D, the user has to deselect some files which he doesn't need.

There are details I want to point:

  • Some files are redundant (ZenLogo.png, ExampleObjectGraph.png) or not really useful (github.css, and maybe README.URL)
  • Those files should be in a place where it is easier for the user to identify if he can avoid importing it without risking breaking anything.
  • The user might wonder if he needs to include the internal folder. Is it used by the library?

zenject-import

Injecting to disabled elements

I always find myself disabling elements that i need disabled when they start so the injection works properly.
Is there any way to be able to inject dependencies into disabled objects?

How to Inject to additive Loaded Content or Scene switch ?

Question

How do I Inject Bindings into a scene that is loaded additive ?

Current scenario

I have the my main StateMachine for the Game ( that handles all state and Scene switches )
Its created by my installer in the application Warmup like this :

Container.Bind<GameStateMachine>().ToSingleFromPrefab<GameStateMachine>(_gameStateMachinePrefab); 

To keep loading times short my warm-up scene is nearly empty ( Installer + Loading Grafics )
From this point I load the needed scene async and additive ( while I can show loading animations etc. )

How do I Inject my main stateMachine ( or any other binding ) to this additive loaded scenes ?

I tried:

[PostInject]
public void Init(GameStateMachine gameStateMachine){
    this.gameStateMachine = gameStateMachine; 
}

but it doesn't get called at all.

So what is the recommended approach for this ? Thanks for your help.

Friedrich Wessel

Resources/ZenjectGlobalCompositionRoot or Resources/ZenjectGlobalInstallers?

Hi there!

The following two source files seem to address a different global installer asset:

In my project I just assumed the name 'ZenjectGlobalInstallers' by renaming the generated asset.

Readme contains outdated info about DiContainer.Instantiate usage

While I was following the readme and tried to create a custom factory with manual instantiation of objects I found out that Instantiate method is not there.
Checked file history. It seems that it is now called InstantiateInternal and it is not public. There is InstantiateExplicit method but there are no examples on how to use it (using it like described in readme doesn't work).

SampleGame example loses asteroid tag after first run

EDIT: This seems to only be a problem with the Asset Store version.

When opening the Asteroids-scene and running it the asteroid-tag on the Asteroid-prefab disappears so on consecutive runs the following Assert fails in ShipStateMoving.cs:

public override void OnTriggerEnter(Collider other)
{
    Assert.That(other.tag == "asteroid");
   _ship.ChangeState(ShipStates.Dead, _ship);
}

Reopening the project resets the tag back until you run the scene once again. Unity seems to remove the tag because the tag does not exist in the projects TagManager; which isn't supplied by Zenject since it's not a complete project, only a scene.

I'm thinking that tags shouldn't be used in the sample or the sample should be made into a complete Unity project. Other suggestions?

Unity 5 - RuntimeInitializeOnLoadMethodAttribute

Hey there

Just a heads up, Unity 5 has added a new attribute RuntimeInitializeOnLoadMethodAttribute which allows you to have a main entry point into the game.

Possibly a good place to initialize the global composition root in Unity 5?

How to resolve new instance of transient type and pass in argument?

I am trying to figure out how I would inject two specific instances into a new instance of my SpecialPurchaseMethodProcessor class along with regular injected arguments.

public class DefaultPurchaseMethodProcessorFactory : IPurchaseMethodProcessorFactory {
    ...
    private DiContainer _container;
    private Dictionary<Type, Func<IPurchaseMethodProcessor>> _creators;

    public void Bind<TPurchaseMethod, TPurchaseMethodProcessor>()
        where TPurchaseMethod : IPurchaseMethod
        where TPurchaseMethodProcessor : IPurchaseMethodProcessor
    {
        _creators[typeof(TPurchaseMethod)] = () => _container.Resolve<TPurchaseMethodProcessor>();
    }

    public IPurchaseMethodProcessor CreateProcessor(IPurchasableItem item, IPurchaseMethod method) {
        var creator = _creators[item.GetType()];
        return creator();
    }
}

public sealed class SpecialPurchaseMethodProcessor : IPurchaseMethodProcessor {
    public SpecialPurchaseMethodProcessor(
        // Specific item for which to create processor for:
            IPurchasableItem item,
            IPurchaseMethod method,
        // Injected types as normal:
            IVirtualCurrencyManager currencies
    ) {
        ...
    }
    ...
}

So this would be consumed as follows:

var processor = factory.CreateProcessor(jellyBeansItem, virtualCurrencyPurchaseMethod);
if (processor.CanPurchase)
    processor.InitiatePurchase();

Does Zenject provide a way to solve this problem? or am I just going about this in the wrong way?

Suggestion for optional parameters with constructor injection

I've bumped into this a couple of times and feel that it would be quite nice if Zenject was able to determine whether a constructor dependency was optional when a default parameter value is present:

public class Foo {
    ...
    public Foo(IBar bar, IBaz baz = null) {
        ...
    }
}

Rather than just throwing the following exception:

ZenjectResolveException: Unable to resolve type 'IBaz' while building object with type 'Foo'. 

Ideally I would prefer to avoid annotating these parameters with an attribute since this can already be deduced using reflection like the following:

if (injectedParameterValue == null) {
    bool isOptionalParameter = parameterInfo.IsOptional || ReflectionUtility.HasZenjectOptionalAttribute(parameterInfo);
    if (!isOptionalParameter)
        throw new ZenjectResolveException();

    // `Type.Missing` is suggested in MSDN (probably to avoid boxing on value types):
    // https://msdn.microsoft.com/en-us/library/system.type.missing(v=vs.110).aspx
    injectedParameterValue = Type.Missing;
}

Is this something that can be added?

Many thanks!

Factory, privat constructor

I want to avoid other programmers to create new instances of my class by using the new word.
Therefore I create factory classes inside all of my entity classes and provide a private standard constructor private Entity(){} only to avoid instantiation via new but force them to use the factory instead.

public class Furniture {
    FurnitureView view;
    Player player;

   //enforces that nobody can create an instance via "new" from outside
   private Furniture() {}

    //Internal class, inherite from Factory<T,U> to enforce Create method to expect T,U parameters
    public class Factory : Factory<FurnitureView, Player, Furniture> {
    }
}

Now Zenject can't inject the view and player
My workaround is to just override create

public class Factory : Factory<FurnitureView, Player, Furniture> {
            public override Furniture Create(FurnitureView view, Player owner) {
                var res = base.Create(view, owner);
                res.View = view;
                res.Player = owner;
                return res;
            }
        }

But then Zenject will recognize the passed parameters as unnecessary
Error 1 ZenjectResolveException: Passed unnecessary parameters when injecting into type 'Furniture'.

So how can I solve this without creating a public constructor public Furniture(FurnitureView view, Player owner){...} that serves for Zenject's constructor injection?

General improvement of the API when it comes to factories

Currently, we have two factory implementations: IFactory/Factory and FactoryTyped. FactoryTyped is the preferable approach, since it is strongly typed (unlike IFactory) and is also automatically validated (an example can be found here). It should therefore probably become the standard factory class so we may want to rename it to just Factory during the next breaking version update

Both approaches have the issue that null arguments result in an exception being thrown. This is because both approaches rely on the type information to figure out which field/parameter to inject into. This is problematic when you actually do want to inject a null value so we need to address that. It is also an issue when you want to inject multiple instances of the same type into a class via factory arguments.

So I think we may want to use the ordering information provided to the factories to handle these cases.

What means: Expected contract type to be non-abstract ?

We get following error when validating the szene :

ZenjectResolveException: Expected contract type 'IControllerStrategy' to be non-abstract

With following code:

Container.Bind<ControllerStrategyFactory>().ToSingle();
public class ControllerStrategyFactory : Factory<StrategyId, IModel_Writeable, IControllerStrategy>
    {
        public override IControllerStrategy Create (StrategyId strategyId, IModel_Writeable model) {

What do we need to change ?
Thanks in advance
Best regard
Marcel Schmidt

Compiles in Unity but does not compile in MonoDevelop

Tried current version of Zenject from the Asset Store (2.4 (Apr 21, 2015)). It compiles fine in Unity itself, but fails to compile in MonoDevelop.

CS0241 - Default parameter specifiers are not permitted... etc. This error is obvious: MonoDevelop is defaulted to Mono/.Net 3.5, where they are just not implemented.

There are two possibilities to overcome the error:

  • change methods with default params to overloaded methods,
  • or just increase the target framework version (as far as I understand, this automatically increases the language version, which otherwise is not possible manually).

The first one works like a charm, of course. And, in my opinion, it is currently necessary not to use default parameters in the assets at all, as long as MonoDevelop is defaulted to 3.5. So, you probably need to fix this...

But there's more: when I try the second workaround the CS0241 error is gone, but I get a bunch of CS0104 ambiguous references between System and ModestTree Func and Tuple. Here I'm not really sure how to deal with this, since C# and Mono/.NET don't provide version defines, which are desired for such situation. Possibly a good idea is to create a custom define, so we could switch between ModestTree to System versions manually, when necessary.

Custom setup after bindings have been installed and validated?

Looking at the MonoInstaller class I have not been able to deduce whether this is already possible.

Essentially this is the sort of thing I was looking for:

public sealed class Installer : MonoInstaller, IInitializable {
    public override void InstallBindings() { ... }
    void IInitializable.Initialize() {
        // Do some initial setup whilst taking advantage of all available bindings...
    }
}

Cheers!

[Object Graph Output] "Unable to locate GraphViz" message on OSX and unable to manually point to non-Windows executable.

Error

Unable to locate GraphViz. Please select the graphviz 'dot.exe' file which can be found at [GraphVizInstallDirectory]/bin/dot.exe. If you do not have GraphViz you can download it at http://www.graphviz.org

Context

I was testing out the Object Graph Output feature of Zenject. The ObjectGraph.dot file is still generated correctly and it is still possible to manually generate the image from the dot file with the following terminal command:

dot -T png ObjectGraph.dot -o ObjectGraph.png

Proposition / Solution

I think it would be interesting to have some compile time conditions to change the default behaviour seen here to something more multiplatform in the future:
https://github.com/modesttree/Zenject/blob/master/UnityProject/Assets/Zenject/Main/Editor/ZenEditorUtil.cs#L179

Something along the lines of (or different/cleaner solutions, like condition on the path EditorPrefs key?):

if UNITY_EDITOR_WIN

var dotExecPath = EditorPrefs.GetString("Zenject.GraphVizDotOSXPath", "");

elif UNITY_EDITOR_OSX

var dotExecPath = EditorPrefs.GetString("Zenject.GraphVizDotExePath", "");

endif

The main problem would be to detect where graphviz is installed I guess? I installed it using Homebrew (brew install graphviz) and here is where the file resides for me:

โžœ UnityProject git:(master) โœ— which dot
/usr/local/bin/dot

The OpenFilePanel does not seem to allow the user to navigate to that path on my system, so manual executable path selection would not be possible on OSX but I think it would also not be necessary since according to this article, the default install path would be this in most cases:

Default Path: Linux / Mac OSX
/usr/local/bin/dot or /usr/bin/dot

This is obviously not a major issue in my opinion and this bug report would allow any OSX user to generate the output image using the command specified up above. Let me know if you have any questions for testing a fix for this on OSX.

How to get singleton instance

Is it possible to get a singleton instance with the help of some function?

I have different implementations for keyboard input and I have a binding like this

public interface IKeyboard
{
    bool IsKeyPressed(KeyCode key);
}

...

_container.Bind<IKeyboard>().ToSingle<MyKeyboard>();

Is it possible to call the IsKeyPressed()-method in a class without having an injected member variable?

[Inject]
private IKeyboard _keyboard

...

if(_keyboard.IsKeyPressed(KeyCode.A)
    ...

I.e. something like this:

if(Zenject.GetSingleton<IKeyboard>.IsKeyPressed(KeyCode.A))
    ...

Support for nested composition roots (contexts)

I am making a spaceship game which currently doesn't use DI, but I have read a lot about Zenject and would like to use it. Some basic info about my game:

  • Each player has one ship.
  • Ships can be docked inside bigger ships.
  • Local co-op support.

I was thinking about making each ship a composition root - that way I could inject ship configuration, player input etc. to the ship's submodules (which are sub transforms of the ship). The ship may be nested when docked.
This does however not seem to be entirely achievable using Zenject.
How would you use Zenject in this case?

Object Pools

It would be extremely helpful to have pools built into Zenject. Any type of object that needs to be created often (bullets, enemies, particles, events) would benefit greatly. My current project would fall flat on its face if I used Zenject factories as-is.

Honestly, I'm not very familiar with IoC or Zenject, so if anything here doesn't make sense, I apologize.

Pool bindings would be written like this:

// Monobehaviour to gameobject pool
_container.BindPool<Asteroid>(SceneSettings.Asteroid.Prefab, size, inflationType, overflowType);
// Short for:
_continer.Bind<IPool<Asteroid>>().To(
    new GameObjectPool<Asteroid>(_container, Settings.Asteroid.Prefab, size, inflationType, overflowType));

// Concrete class to pool
_container.BindPool<Enemy>(size, inflationType, overflowType);
// Short for:
_container.Bind<IPool<Enemy>>().ToSingle<Pool<Enemy>>(size, inflationType, overflowType);

// Prefab to pool
_container.Bind<IPool<Player>>().ToSingleFromPrefab<Pool<Player>>(Settings.Player.Prefab, size, inflationType, overflowType);

.BindPool() would be defined in DiContainer as generic a method with one and two types along with overrides for standard and prefab pools. I'm not quite sure how you would handle the arguments for BindPool internally.

The classes Pool and GameObjectPool would be written custom factories similar to Factory and GameObjectFactory.

Example usage:

public class Enemy : IPoolable
{
    public Action<Enemy> OnDeath = delegate { };

    public Enemy() { }

    #region IPoolable Implementation

    // Reset instance to default values
    public void Restore() 
    { 
        OnDeath = delegate { };
    }

    #endregion

    private void Die()
    {
        OnDeath(this);
    }
}

public class EnemySpawner
{
    private IPool<Enemy> pool;

    public EnemySpawner(IPool<Enemy> pool)
    {
        this.pool = pool;
    }

    public Enemy Create(params object[] constructorArgs)
    {
        var enemy = pool.Create(constructorArgs);
        enemy.OnDeath += OnEnemyDeath;

        return enemy;
    }

    private void OnEnemyDeath(Enemy enemy)
    {
        pool.ReturnInstance(enemy);
    }
}

Fallback Containers and Optional Injectables

It looks like you guys decided to make it so 'optional' contexts will not fall back to 'parent' containers should they fail to find an appropriate binding in the base container. Is there any reason you decided to go that way?

I'm working on some experimental code using Zenject and it seems to me like it would be far more helpful if DiContainers searched fallbacks for optional bindings the same as they do for mandatory ones (simply returning null if no bindings are found therein).

Something wrong with order of execution

I've been using Zenject version that I downloaded at 15th of August and everything is fine with it.
Today I tried to update to the latest version and it didn't work.

  1. Property injection was not done when OnEnable fired. Readme states that everything is done during Awake(), so everything should have been already injected.
  2. IInitializable::Initialize() method was called AFTER Start(), which again shouldn't be the case if readme is to be believed. I had an event fired in Start() before IInitializable class had a chance to subscribe to it.

I reverted to my old version of Zenject and everything works fine.

Lots of per frame allocations relating to use of string formatting

Looking at the Unity profiler I am seeing that Zenject is generating garbage per frame. As I am sure you know, garbage collection performance in Unity is horrible and mid-level allocations should be avoided, especially per frame.

The biggest culprits are the two tickable handlers:

void UpdateTickable(ITickable tickable)
{
    // - Allocation for use of string formatting.
    // - Allocation for use of 'params' keyword (equivalent to new object[] { arg1, arg2 }).
    using (ProfileBlock.Start("{0}.Tick()".With(tickable.GetType().Name())))
    {
        tickable.Tick();
    }
}

Removing ProfileBlock avoids some of this garbage:

void UpdateTickable(ITickable tickable)
{
    tickable.Tick();
}

I propose that the above is changed to the following:

void UpdateTickable(ITickable tickable)
{
    // Allow `ProfileBlock.Start` to perform formatting when enabled.
    // Instead of accepting `params object[]` provide several overloads to
    // accept a fixed number of parameters.
    using (ProfileBlock.Start("{0}.Tick()", tickable.GetType().Name()))
    {
        tickable.Tick();
    }
}

For testing purposes I removed the ProfileBlock from these two functions and performance was instantly better.

TaskUpdater is another culprit of per frame allocations due to use of System.Linq there are internal allocations plus enumerator allocations.

Changing the following:

public void UpdateRange(int minPriority, int maxPriority)
{
    // Make sure that tasks with priority of int.MaxValue are updated when maxPriority is int.MaxValue
    foreach (var taskInfo in _sortedTasks.Where(x =>
        !x.IsRemoved && x.Priority >= minPriority && (maxPriority == int.MaxValue || x.Priority < maxPriority)))
    {
        Assert.That(taskInfo.Priority.HasValue);
        _updateFunc(taskInfo.Task);
    }

    ClearRemovedTasks(_sortedTasks);
}

to the following instantly helped to improve performance:

public void UpdateRange(int minPriority, int maxPriority)
{
    // Make sure that tasks with priority of int.MaxValue are updated when maxPriority is int.MaxValue
    foreach (var taskInfo in _sortedTasks)
    {
        if (!taskInfo.IsRemoved && taskInfo.Priority >= minPriority && (maxPriority == int.MaxValue || taskInfo.Priority < maxPriority))
        {
            Assert.That(taskInfo.Priority.HasValue);
            _updateFunc(taskInfo.Task);
        }
    }

    ClearRemovedTasks(_sortedTasks);
}

For some reason this foreach statement is also allocating an enumerator despite the fact that LinkedList<T> defines a struct enumerator.

Looking at the profiler there are several other places where Zenject is allocating per frame.

Multiple constructors without tagging

Are there any plans to support multiple constructors without having to resort to the [Inject] keyword? Ninject and StrangeIoC use strategies to resolve the best matching constructor. E.g.: Least arguments, constructor that has most injectable parameters.

This would allow me not tie to myself to the zenject assemblies in the implementation.

Unity integration testing

I see how CompositionRoot does it's thing and it works fine in play mode, but not so much when run in Unity's integration test runner. If it is possible to make this work as it stands, it would be great to mention it into the docs.

Remove ModestTree namespace

Given that Zenject is open source and not proprietary to ModestTree it should really be in its own namespace.

Support for circular dependencies

Currently Zenject throws exceptions when it discovers circular dependencies. This makes sense for constructor injection, but should be possible in theory for field/property/postinject injection.

Add support for convention based binding

In some cases it can be tedious to, for example, add every binding to ITickable or IInitializable explicitly in the installer. What would be useful is if you could define generic conventions to always handle cases like this. We could use another fluent interface, something like the following (which is largely taken from what other DI frameworks look like)

Container.Bind(x => x.AllAssemblies.AllClasses().WhichInheritFrom<ITickable>())
    .InterfaceToSingle()); 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.