Giter Club home page Giter Club logo

Comments (11)

pbastia avatar pbastia commented on June 27, 2024 1

Hi,
I ended up writing this method, because as @svermeulen mentioned, open types are not being handled quite correctly.
it works the same as x.ConnectImplementationsToTypesClosing() in StructureMap.

Not sure if this is something that could be used inside the Zenject code, but it works for my purpose:

public static void BindAllOpenGenericInterfacesToConcreteImplementation(this DiContainer container, Type TGenericInterface, string targetNamespace = "")
{
	// AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
	// Might be safer, we are not sure what is the calling assembly.
	var types = Assembly.GetExecutingAssembly().GetTypes();
			
      //WARNING without the distinct clause we end up binding twice
	var implementedInterfaces = types.SelectMany(type => type.GetInterfaces()).Where(iface => 
                iface.IsGenericType && iface.GetGenericTypeDefinition() == TGenericInterface).Distinct();
	var targetTypes = string.IsNullOrEmpty(targetNamespace) ? types : types.Where(t => t.Namespace == targetNamespace);
			
	foreach (var implementedInterface in implementedInterfaces)
	{
		var typesImplementing = targetTypes.Where(t => implementedInterface.IsAssignableFrom(t) && !t.IsInterface);
        
		container.Bind(implementedInterface).To(typesImplementing).AsSingle();
	}
}

I use it as follow:

this.Container.BindAllOpenGenericInterfacesToConcreteImplementation(typeof(INotificationHandler<>));

from zenject-2019.

janus007 avatar janus007 commented on June 27, 2024

It is an very important feature to have in a DI-framework which was one of the reasons I immediately felt in love with Structuremap 10 years ago :) , Autofac and other major DI-frameworks has it as well now.

The reason for scanning and/ or convention-based binding is that polymorphism is working flawlessly making it even more powerful to utilize a DI-framework.

One more thing to mention regarding binding, is the fantastic feature called open generics, this is where the world of magic opens up and you wonder if you ever will wake up from the dream.

from zenject-2019.

svermeulen avatar svermeulen commented on June 27, 2024

Yep, this is supported in Zenject 4, along with lots of other improvements, which is just a few days away from being ready to use.

As for open generics, I'm assuming you're talking about doing stuff like this?

Container.Bind(typeof(List<>)).AsSingle();

var stringList = Container.Resolve<List<string>>();

This has been supported for a long time but I haven't personally used it much. Maybe there are some interesting design choices with it that I'm missing

By the way, if you are already familiar with StructureMap, AutoFac, etc. I would love to hear more feedback :) Before working in Unity3D I only really used Ninject so I don't have much else to compare it to

from zenject-2019.

janus007 avatar janus007 commented on June 27, 2024

Sorry, for the delay. I somehow missed the reply.
Anyway... you're right on target with:
Container.Bind(typeof(List<>)).AsSingle();

For readers: suppose we have different implementations of EnemyBrain:

interface IEnemyBrain<T>
void Think(T go);

and:

class EnemyBrain: IEnemyBrain<Clever>
void Think(Clever clever)
{
   clever.ExtendedPositionAwareness(....
}

class EnemyBrain: IEnemyBrain<Idiot>
void Think(Idiot idiot)
{
   idiot.SimpleStupidPositionAwareness(....
}

We could then do something like:

class Enemy(IEnemyBrain<Clever> brain)
{
void Update()
   brain.Think();
}

class Enemy(IEnemyBrain<Idiot> brain)
{
void Update()
   brain.Think()
}

Seems a bit tedious at first, but the smart thing is that all the wiring is done automatically and follow separation of concerns nicely.

from zenject-2019.

janus007 avatar janus007 commented on June 27, 2024

Hello svermeulen

Just tested this with generics but couldn't get it to work. Validation failed.

public interface IFoo<T>
        {
            string Bar(T t);
        }

        public class FooString:IFoo<string>
        {
            public string Bar(string s)
            {
                return s;
            }
        }
        public class FooInt : IFoo<int>
        {
            public string Bar(int s)
            {
                return s.ToString();
            }
        }

Install Bindings:

   Container.Bind(typeof(IFoo<>)).AsSingle();

   var expectedInstanceOfFooString = Container.Resolve<FooString>();
   var expectedInstanceOfFooString = Container.Resolve<IFoo<string>>();
   var expectedInstanceOfFooInt = Container.Resolve<IFoo<int>>();

Cannot resolve any of them. Maybe the binding parameters are wrong?

Not sure if this can help, but normally I do like this in StructureMap:http://structuremap.github.io/generics/ , pay attention to : x.ConnectImplementationsToTypesClosing(typeof(IVisualizer<>));

Of course I can do the mapping by hand, but it would be really nice to have support for open generics scanning.

from zenject-2019.

svermeulen avatar svermeulen commented on June 27, 2024

Ok I looked into this a bit.

This binding:

Container.Bind(typeof(IFoo<>)).AsSingle();

Doesn't make any sense given the way the Zenject interprets it. That means that Zenject should instantiate an instance of the interface IFoo<> which is impossible.

In Zenject you have to explicitly declare every mapping from the "contract type" to the "concrete instantiated type". So I think you're trying to do this:

var container = new DiContainer();

container.Bind(typeof(IFoo<>)).To(x => x.AllTypes().DerivingFrom(typeof(IFoo<>))).AsSingle();
container.Bind(x => x.AllTypes().DerivingFrom(typeof(IFoo<>))).AsSingle();

var expectedInstanceOfFooString = container.Resolve<FooString>();
var expectedInstanceOfFooString2 = container.Resolve<IFoo<string>>();
var expectedInstanceOfFooInt = container.Resolve<IFoo<int>>();

This code almost works, except that the DerivingFrom method does not handle open generic types properly. I'll leave this item open to address that.

from zenject-2019.

janus007 avatar janus007 commented on June 27, 2024

Excellent...
You are very helpful :)

from zenject-2019.

svermeulen avatar svermeulen commented on June 27, 2024

Actually you could do it this way too:

var container = new DiContainer();

container.Bind(typeof(IFoo<>)).To(x => x.AllNonAbstractClasses()).AsSingle();
container.Bind(x => x.AllTypes().DerivingFrom(typeof(IFoo<>))).AsSingle();

var runner = container.Instantiate<Runner>();
var expectedInstanceOfFooString = container.Resolve<FooString>();
var expectedInstanceOfFooString2 = container.Resolve<IFoo<string>>();
var expectedInstanceOfFooInt = container.Resolve<IFoo<int>>();

When doing convention based binding, Zenject will throw away any bindings that do not derive from the contract type, so you can just bind to AllNonAbstractClasses instead of the specific derived types

But again, there's an issue with open types not being handled correctly when used with interfaces somewhere that needs to be fixed first

from zenject-2019.

svermeulen avatar svermeulen commented on June 27, 2024

I committed a fix just now that allows things like this to work:

Container.Bind(typeof(IFoo<>)).To(typeof(Foo<>)).AsSingle()

from zenject-2019.

Vorlex avatar Vorlex commented on June 27, 2024

Hi!
I've tested with this bindings:

public class MainInstaller : MonoInstaller<MainInstaller>
{
    public override void InstallBindings()
    {
        Container.Bind<IA>().To<A>().AsTransient();
        Container.Bind<IB>().To<B>().AsTransient();

        Container.Bind(typeof(IFoo<>)).To(typeof(Foo<>)).AsSingle();
        Container.Bind<Tester>().ToSelf().AsSingle().NonLazy();
    }

    public class Tester
    {
        public Tester(IFoo<IA> a, IFoo<IB> b){}
    }

    public class A : IA {}
    public class B : IB {}
    public interface IB {}
    public interface IA{}
    public class Foo<T> : IFoo<T>
    {
        public Foo()
        {
            Debug.LogWarning(typeof(T));
        }
    }
    public interface IFoo<T>{}
}

and it throws this exception on resolving IFoo<IB>:

ArgumentException: failed to convert parameters
System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:484)
System.Reflection.MonoCMethod.Invoke (BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:528)
System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/ConstructorInfo.cs:77)
Zenject.DiContainer.InstantiateInternal (System.Type concreteType, Boolean autoInject, Zenject.InjectArgs args) (at Assets/Plugins/Zenject/Source/Main/DiContainer.cs:962)
Rethrow as ZenjectException: Error occurred while instantiating object with type 'MainInstaller+Tester'
Zenject.DiContainer.InstantiateInternal (System.Type concreteType, Boolean autoInject, Zenject.InjectArgs args) (at Assets/Plugins/Zenject/Source/Main/DiContainer.cs:967)
Zenject.DiContainer.InstantiateExplicit (System.Type concreteType, Boolean autoInject, Zenject.InjectArgs args) (at Assets/Plugins/Zenject/Source/Main/DiContainer.cs:2477)
Zenject.TransientProvider+<GetAllInstancesWithInjectSplit>c__Iterator0.MoveNext () (at Assets/Plugins/Zenject/Source/Providers/TransientProvider.cs:62)
Zenject.CachedProvider+<GetAllInstancesWithInjectSplit>c__Iterator0.MoveNext () (at Assets/Plugins/Zenject/Source/Providers/CachedProvider.cs:50)
Zenject.IProviderExtensions.GetAllInstances (IProvider creator, Zenject.InjectContext context, System.Collections.Generic.List`1 args) (at Assets/Plugins/Zenject/Source/Providers/IProviderExtensions.cs:29)
Zenject.IProviderExtensions.GetAllInstances (IProvider creator, Zenject.InjectContext context) (at Assets/Plugins/Zenject/Source/Providers/IProviderExtensions.cs:18)
Zenject.DiContainer.SafeGetInstances (Zenject.ProviderPair providerPair, Zenject.InjectContext context) (at Assets/Plugins/Zenject/Source/Main/DiContainer.cs:819)
Zenject.DiContainer.ResolveDependencyRoots () (at Assets/Plugins/Zenject/Source/Main/DiContainer.cs:241)
Zenject.SceneContext.Resolve () (at Assets/Plugins/Zenject/Source/Install/Contexts/SceneContext.cs:268)
Zenject.SceneContext.RunInternal () (at Assets/Plugins/Zenject/Source/Install/Contexts/SceneContext.cs:137)
Zenject.RunnableContext.Run () (at Assets/Plugins/Zenject/Source/Install/Contexts/RunnableContext.cs:36)
Zenject.RunnableContext.Initialize () (at Assets/Plugins/Zenject/Source/Install/Contexts/RunnableContext.cs:22)
Zenject.SceneContext.Awake () (at Assets/Plugins/Zenject/Source/Install/Contexts/SceneContext.cs:113)

So I guess it still doesn't work.

from zenject-2019.

svermeulen avatar svermeulen commented on June 27, 2024

@Vorlex Your example runs now on develop branch, thanks for the report

As far as I know the issues with open generics are fixed now so closing this

from zenject-2019.

Related Issues (20)

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.