VContainer icon indicating copy to clipboard operation
VContainer copied to clipboard

[Feature Request] Lazy inject

Open SettingDust opened this issue 1 year ago • 10 comments

Like the https://github.com/hadashiA/VContainer/issues/508#issuecomment-1519579824 says. I think lazy inject should be a thing. Syntax can be like

[Inject] internal Lazy<T> LazyInjected

SettingDust avatar May 22 '23 07:05 SettingDust

+1, Also with UniTask inject AsyncLazy<T>?

AlonTalmi avatar Jun 05 '23 07:06 AlonTalmi

I think when using [Inject] attribute on a property / field, this is already lazy. In my understanding vcontainer does everything lazy (resolve it, when you need it).

I think when #508 would have used property / field injection for the statemanager and the rest in the constructor instead of method injection, it might have worked out. The statemanager can be build completely and the first time the bootstate would need the statemanager, it would then resolve it.

But in general, you should avoid circular dependencies, since this is at least for me a code smell. There are some patterns to solve circular dependencies, e.g. an aggregate for coupling on top.

lolhans avatar Jan 07 '24 03:01 lolhans

VContainer creates the instances at the moment of injection @lolhans

AlonTalmi avatar Jan 07 '24 08:01 AlonTalmi

I need to admit that Im not 100 percent sure, but I think using the [Inject] attribute for property injection in VContainer can indeed help resolve circular dependencies that occur with constructor injection. This is because property injection allows the creation of instances without needing all dependencies to be resolved upfront, as is the case with constructor injection.

Here's how it works:

Constructor Injection: When you use constructor injection, all dependencies of a class must be resolved before the class itself can be instantiated. If Class A depends on Class B and vice versa, this creates a circular dependency that cannot be resolved, leading to an error.

Property Injection: With property injection, instances of classes can be created even if not all dependencies are immediately available. After the instances are created, the dependencies are then "injected" into the properties marked with the [Inject] attribute. This allows VContainer to first create instances of both Class A and Class B, and then resolve the dependencies.

Here's a basic example:

public class ClassA
{
    [Inject]
    public ClassB B { get; set; }
}

public class ClassB
{
    [Inject]
    public ClassA A { get; set; }
}

In this setup, I think VContainer can create instances of ClassA and ClassB without immediately needing the other, and then inject the dependencies afterward, when it is needed the first time.

Unfortunately I cannot verify this approach, since I have no computer available right now. But I think I have done this in a recent project. Could someone please check this out? @AlonTalmi

lolhans avatar Jan 07 '24 08:01 lolhans

It won't work, while makes sense and technically can be supported, that's not the case and I guess it's by design.

The injection and resolution systems are completely separate. Injection is as simple as going over all the type's members and calling Resolve(memebrType) from the container, there is no separate CreateInstance() method and Inject() method.

Also if I might add, circular dependency is usually considered code smell whether it's supported or not.

AlonTalmi avatar Jan 07 '24 09:01 AlonTalmi

Okay, when you have actually tried that, you are propably right on that. Yeah, I already mentioned in a comment earlier, that you should avoid circular dependency in general.

lolhans avatar Jan 07 '24 14:01 lolhans

You're right, I apologize!

AlonTalmi avatar Jan 07 '24 14:01 AlonTalmi

Hey @SettingDust, based on our discussion here, do you think the approach with property injection and the [Inject] attribute addresses your concern regarding Lazy Injection in VContainer?

lolhans avatar Jan 07 '24 16:01 lolhans

It is always possible to introduce a separate util class like "GameSystem" or any name to avoid circular dependencies. But I don't think it's the solution to lazy inject. The lazy injection I propose here leans toward framework design rather than "What should I do"

SettingDust avatar Jan 08 '24 03:01 SettingDust

Maybe I wasn't clear enough. I think using a mix of cunstructor injection and (lazy) field / property injection already does the job of breaking the circular dependency. Just move out the circular dependency from constructor to a property and add the Inject attribute to it.

lolhans avatar Jan 08 '24 09:01 lolhans