diff --git a/README.md b/README.md index de3faddc..e5a27d19 100644 --- a/README.md +++ b/README.md @@ -9,36 +9,27 @@ "V" means making Unity's initial "U" more thinner and solid ... ! -- Fast resolve. Minimum GC allocation. -- Extra small code size. Few internal types. -- Application can freely create nested Lifetime Scope. -- IoC : Create script entry point without MonoBehaviour. (Using own Unity's PlayerLoopSystem) -- ECS integration. -- Immutable Container. +- **Fast resolve:** Basically, 5-10x faster than Zenject. +- **Minimum GC allocation**: In Resolve, We have zero allocation without spawned instances. +- **Extra small code size**: Few internal types and few .callvirt. +- **Flexible scoping**: Application can freely create nested Lifetime Scope. +- **IoC**: Create script entry point without MonoBehaviour. (Using own Unity's PlayerLoopSystem) +- **ECS integration** +- **Immutable Container:** Thread safety and robustness. ![](docs/benchmark_result.png) -Basically, VContainer is 5-10x faster than Zenject. - - By default, both VContainer and Zenject use reflection at runtime. - "VContainer (CodeGen)" means optimization feature by pre-generation IL code of Inject methods by ILPostProcessor. - See [Optimization](#optimization) section more information. -- Zenject also has a pre-code generation feature called "Reflection Baking", but it was excluded in this benchmark. (Because it didn't succeed in working in the same environment..) -And in resolve, We have zero allocation (without resolved instances): +Here is an example of profile results for GC Alloc: ![](docs/screenshot_profiler_vcontainer.png) ![](docs/screenshot_profiler_zenject.png) -## Breaking Changes - -- From 0.0.x - - [Remove MonoInstaller/ScriptableObjectInstaller and instead inherit LifetimeScope](https://github.com/hadashiA/VContainer/pull/15) - - If you are using an earlier version, please check [Getting Started](#getting-started) again. -- From 0.2.x - - [Use `VContainerSettings` instead of automatically loading Resources](https://github.com/hadashiA/VContainer/pull/25) - - If you were using "ProjectLifetimeScope" in Resources, please check [How to create project root LifetimeScope](#how-to-create-project-root-lifetimescope) +(VContainer has achieved zero allocation during Resolve.) ## Index @@ -50,6 +41,7 @@ And in resolve, We have zero allocation (without resolved instances): - [Controlling Scope and Lifetime](#controlling-scope-and-lifetime) - [Dispatching Unity Lifecycle](#dispatching-unity-lifecycle) - [Integrating with ECS](#integrating-with-ecs) +- [Comparing VContainer to Zenject](#comparing-vcontainer-to-zenject) - [Optimization](#optimization) - [Best Practices and Recommendations](#best-practices-and-recommendations) @@ -628,9 +620,77 @@ class OtherClass } ``` +#### Register Factory + +VContainer allows to register a `Func<>` delegate for the creation of an instance. This is especially useful in scenarios where instance is created at any time during execution, or multiple instances are created at any time. + +By registering Factory instead of instance, you can keep the dependency static and simple. + +##### Register `Func<>` Factory that requires only runtime parameters + +```csharp +builder.RegisterFactory(x => new Foo(x)); +``` + +We can resolve like below: + +```csharp +class ClassA +{ + readonly Func factory; + + public ClassA(Func factory) + { + this.factory = factory; + } + + public void DoSomething() + { + var foo = factory.Invoke(100); + // ... + } +} +``` + +##### Register `Func<>` Factory that requires container dependencies and runtime parameters + +```csharp +builder.RegisterFactory(container => +{ + var dependency = container.Resolve(); // Resolve per scope + return x => new Foo(x, dependency); // Execute per factory invocation +}, Lifetime.Scoped); +``` + +This method required 2 params + +- Func<>: Receives Container and returns Factory. + +- Lifetime : Determines how often the Factory is generated. (that is, how often the outer Func is executed.) + +We can resolve like below: + +```csharp +class ClassA +{ + readonly Func factory; + + public ClassA(Func factory) + { + this.factory = factory; + } + + public void DoSomething() + { + var foo = factory.Invoke(100); + // ... + } +} +``` + #### Register `MonoBehaviour` -**Register from LifetimeScope's `[SerializeField]`** +##### Register from LifetimeScope's `[SerializeField]` ```csharp [SerializeField] @@ -644,7 +704,7 @@ builder.RegisterComponent(yourBehaviour); Note: - `RegisterComponent` similar to `RegisterInstance`. The only difference is that MonoBehaviour registered with `RegisterComponent` will be injected even if not Resolved. -**Register from scene with `LifetimeScope`** +##### Register from scene with `LifetimeScope` ```csharp builder.RegisterComponentInHierarchy(); @@ -653,7 +713,7 @@ builder.RegisterComponentInHierarchy(); Note: - `RegisterComponentInHierarchy` always `.Scoped` lifetime. Because lifetime is equal to the scene. -**Register component that Instantiate from prefab when resolving** +##### Register component that Instantiate from prefab when resolving ```csharp [SerializeField] @@ -664,20 +724,20 @@ YourBehaviour prefab; builder.RegisterComponentInNewPrefab(prefab, Lifetime.Scoped); ``` -**Register component that with new GameObject when resolving** +##### Register component that with new GameObject when resolving ```csharp builder.RegisterComponentOnNewGameObject(Lifetime.Scoped, "NewGameObjectName"); ``` -**Register component as interface** +##### Register component as interface ```csharp builder.RegisterComponentInHierarchy() .AsImplementedInterfaces(); ``` -**Register component to specific parent Transform** +##### Register component to specific parent Transform ```csharp builder.RegisterComponentFromInNewPrefab(Lifetime.Scoped) @@ -775,12 +835,12 @@ it has following behaviours: - If registered object is not found, `LifetimeScope` will look for a parent `LifetimeScope`. - For `Lifetime.Singleton` - - Always returns the same instance. - - Parent and child cannot register the same type. + - Basically, always returns the same instance. + - If parent and child have the same type, it returns the instance with the closest scope. - For `LifeTime.Transient` - Instance creating for each resolving. - If parent and child have the same type, child will prioritize itself. -- For `Lifetime.Scoped`: +- For `Lifetime.Scoped` - Instance will be different for each child. - If same child, returns same instance. - If parent and child have the same type, child will prioritize itself. @@ -1311,10 +1371,158 @@ Zenject is awesome. but VContainer is: | Container.Bind\()
    .FromNewComponentOnNewGameObject()
    .AsCached()
    .WithGameObjectName("Foo1") | builder.RegisterComponentOnNewGameObject\(Lifetime.Scoped, "Foo1") | | .UnderTransform(parentTransform) | .UnderTransform(parentTransform) | | .UnderTransform(() => parentTransform) | .UnderTransform(() => parentTransform) | - | Container.Bind\()
    .FromComponentInNewPrefabResource("Some/Path/Foo") | **Not supported**
We should load Resources using `LoadAsync` family.
You can use `RegisterInstance()` etc after loading the Resource. | - | Container.Bind\()
    .WithId("foo").AsCached() | **Not supported**
Duplicate type Resolve is not recommended.
You can instead use type-specific Register
builder.Register\(Lifetime.Scoped)
    .WithParameter("foo", foo) | -wip + + + + + + + + + + + + + + + +
ZenjectVContainer
+ +```csharp +Container.Bind() + .FromComponentInNewPrefabResource("Some/Path/Foo") +``` + + + +#### Not supported + +We should load Resources using LoadAsync family. +You can use RegisterInstance() etc after loading the Resource. + +
+ +```csharp +Container.Bind() + .WithId("foo").AsCached() +``` + + + +#### Not supported + +Duplicate type Resolve is not recommended. +You can instead use type-specific Register +builder.Register(Lifetime.Scoped) + .WithParameter("foo", foo) + +
+ + + + + + + + + + + + +
ZenjectVContainer
+ +```csharp +public class Enemy +{ + readonly Player player; + readonly float speed; + + public Enemy(float speed, Player player) + { + this.player = player; + this.speed = speed; + } + + public class Factory : PlaceholderFactory; + { + } +} + +Container.BindFactory(); +``` + + + +```csharp +public class Enemy +{ + readonly Player player; + readonly float speed; + + public Enemy(float speed, Player player) + { + this.player = player; + this.speed = speed; + } +} + +builder.RegisterFactory(container => +{ + var player = container.Resolve(); + return speed => new Enemy(speed, player); +}, Lifetime.Scoped); +``` + +
+ +```csharp +public class Enemy : MonoBehaviour +{ + Player player; + + [Inject] + public void Construct(Player player) + { + this.player = player; + } + + public class Factory : PlaceholderFactory + { + } +} + +Container.BindFactory() + .FromComponentInNewPrefab(enemyPrefab); +``` + + + +```csharp +public class Enemy : MonoBehaviour +{ + Player player; + + public void Construct(Player player) + { + this.player = player; + } +} + +builder.RegisterFactory(container => +{ + var player = container.Resolve(); + return () => + { + var enemy = Instantiate(enemyPrefab); + enemy.Construct(player); + return enemy; + }; +}, Lifetime.Scoped); +``` + +
+ ## Optimization diff --git a/VContainer.StandaloneTests/VContainer.StandaloneTests.csproj b/VContainer.StandaloneTests/VContainer.StandaloneTests.csproj index a4800c35..74a871fc 100644 --- a/VContainer.StandaloneTests/VContainer.StandaloneTests.csproj +++ b/VContainer.StandaloneTests/VContainer.StandaloneTests.csproj @@ -7,10 +7,7 @@ - + diff --git a/VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs b/VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs index b622ead7..4afce854 100644 --- a/VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs +++ b/VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs @@ -9,11 +9,12 @@ namespace VContainer public interface IContainerBuilder { object ApplicationOrigin { get; set; } + bool ContainerExposed { get; set; } RegistrationBuilder Register(Lifetime lifetime); RegistrationBuilder RegisterInstance(object instance); + RegistrationBuilder Register(RegistrationBuilder registrationBuilder); - void RegisterContainer(); IObjectResolver Build(); } @@ -45,11 +46,10 @@ public IScopedObjectResolver BuildScope() public class ContainerBuilder : IContainerBuilder { public object ApplicationOrigin { get; set; } + public bool ContainerExposed { get; set; } readonly IList registrationBuilders = new List(); - bool containerRegistered; - public RegistrationBuilder Register(Lifetime lifetime) => Register(new RegistrationBuilder(typeof(T), lifetime)); @@ -62,8 +62,6 @@ public RegistrationBuilder Register(RegistrationBuilder registrationBuilder) return registrationBuilder; } - public void RegisterContainer() => containerRegistered = true; - public virtual IObjectResolver Build() { var registrations = BuildRegistrations(); @@ -101,7 +99,7 @@ protected IReadOnlyList BuildRegistrations() TypeAnalyzer.CheckCircularDependency(x.ImplementationType); } #endif - if (containerRegistered) + if (ContainerExposed) { registrations.Add(ContainerRegistration.Default); } diff --git a/VContainer/Assets/VContainer/Runtime/ContainerBuilderExtensions.cs b/VContainer/Assets/VContainer/Runtime/ContainerBuilderExtensions.cs index d431deae..80000328 100644 --- a/VContainer/Assets/VContainer/Runtime/ContainerBuilderExtensions.cs +++ b/VContainer/Assets/VContainer/Runtime/ContainerBuilderExtensions.cs @@ -37,6 +37,85 @@ public static RegistrationBuilder RegisterInstance builder.RegisterInstance(instance).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3)); + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + { + var registrationBuilder = new FactoryRegistration(factoryFactory, lifetime); + builder.Register(registrationBuilder); + return registrationBuilder; + } + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + { + var registrationBuilder = new FactoryRegistration(factoryFactory, lifetime); + builder.Register(registrationBuilder); + return registrationBuilder; + } + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + { + var registrationBuilder = new FactoryRegistration(factoryFactory, lifetime); + builder.Register(registrationBuilder); + return registrationBuilder; + } + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + { + var registrationBuilder = new FactoryRegistration(factoryFactory, lifetime); + builder.Register(registrationBuilder); + return registrationBuilder; + } + + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + { + var registrationBuilder = new FactoryRegistration(factoryFactory, lifetime); + builder.Register(registrationBuilder); + return registrationBuilder; + } + + + public static void RegisterContainer(this IContainerBuilder builder) + => builder.ContainerExposed = true; + public static RegistrationBuilder RegisterGCCollect(this IContainerBuilder builder) => builder.RegisterInstance(new GCCollectScope()); } diff --git a/VContainer/Assets/VContainer/Runtime/IRegistration.cs b/VContainer/Assets/VContainer/Runtime/IRegistration.cs new file mode 100644 index 00000000..7cb79f8b --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/IRegistration.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace VContainer +{ + public interface IRegistration + { + Type ImplementationType { get; } + IReadOnlyList InterfaceTypes { get; } + Lifetime Lifetime { get; } + object SpawnInstance(IObjectResolver resolver); + } +} diff --git a/VContainer/Assets/VContainer/Runtime/Registration.cs.meta b/VContainer/Assets/VContainer/Runtime/IRegistration.cs.meta similarity index 100% rename from VContainer/Assets/VContainer/Runtime/Registration.cs.meta rename to VContainer/Assets/VContainer/Runtime/IRegistration.cs.meta diff --git a/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs b/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs new file mode 100644 index 00000000..46001ed8 --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; + +namespace VContainer.Internal +{ + sealed class FactoryRegistration : RegistrationBuilder, IRegistration + { + readonly Func> factoryFactory; + + internal FactoryRegistration(Func> factoryFactory, Lifetime lifetime) + : base(typeof(Func), lifetime) + { + this.factoryFactory = factoryFactory; + } + + public new Type ImplementationType => base.ImplementationType; + public new IReadOnlyList InterfaceTypes => null; + public new Lifetime Lifetime => base.Lifetime; + + public override IRegistration Build() => this; + public object SpawnInstance(IObjectResolver resolver) => factoryFactory(resolver); + } + + sealed class FactoryRegistration : RegistrationBuilder, IRegistration + { + readonly Func> factoryFactory; + + internal FactoryRegistration(Func> factoryFactory, Lifetime lifetime) + : base(typeof(Func), lifetime) + { + this.factoryFactory = factoryFactory; + } + + public new Type ImplementationType => base.ImplementationType; + public new IReadOnlyList InterfaceTypes => null; + public new Lifetime Lifetime => base.Lifetime; + + public override IRegistration Build() => this; + public object SpawnInstance(IObjectResolver resolver) => factoryFactory(resolver); + } + + sealed class FactoryRegistration : RegistrationBuilder, IRegistration + { + readonly Func> factoryFactory; + + internal FactoryRegistration(Func> factoryFactory, Lifetime lifetime) + : base(typeof(Func), lifetime) + { + this.factoryFactory = factoryFactory; + } + + public new Type ImplementationType => base.ImplementationType; + public new IReadOnlyList InterfaceTypes => null; + public new Lifetime Lifetime => base.Lifetime; + + public override IRegistration Build() => this; + public object SpawnInstance(IObjectResolver resolver) => factoryFactory(resolver); + } + + sealed class FactoryRegistration : RegistrationBuilder, IRegistration + { + readonly Func> factoryFactory; + + internal FactoryRegistration(Func> factoryFactory, Lifetime lifetime) + : base(typeof(Func), lifetime) + { + this.factoryFactory = factoryFactory; + } + + public new Type ImplementationType => base.ImplementationType; + public new IReadOnlyList InterfaceTypes => null; + public new Lifetime Lifetime => base.Lifetime; + + public override IRegistration Build() => this; + public object SpawnInstance(IObjectResolver resolver) => factoryFactory(resolver); + } + + sealed class FactoryRegistration : RegistrationBuilder, IRegistration + { + readonly Func> factoryFactory; + + internal FactoryRegistration(Func> factoryFactory, Lifetime lifetime) + : base(typeof(Func), lifetime) + { + this.factoryFactory = factoryFactory; + } + + public new Type ImplementationType => base.ImplementationType; + public new IReadOnlyList InterfaceTypes => null; + public new Lifetime Lifetime => base.Lifetime; + + public override IRegistration Build() => this; + public object SpawnInstance(IObjectResolver resolver) => factoryFactory(resolver); + } +} diff --git a/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs.meta b/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs.meta new file mode 100644 index 00000000..aebea0ab --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Internal/FactoryRegistration.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c959432c5cd54ca1abab288ed70c4d75 +timeCreated: 1602688682 \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Registration.cs b/VContainer/Assets/VContainer/Runtime/Internal/Registration.cs similarity index 89% rename from VContainer/Assets/VContainer/Runtime/Registration.cs rename to VContainer/Assets/VContainer/Runtime/Internal/Registration.cs index dbf8557d..9037ee2b 100644 --- a/VContainer/Assets/VContainer/Runtime/Registration.cs +++ b/VContainer/Assets/VContainer/Runtime/Internal/Registration.cs @@ -1,19 +1,10 @@ using System; using System.Collections; using System.Collections.Generic; -using VContainer.Internal; -namespace VContainer +namespace VContainer.Internal { - public interface IRegistration - { - Type ImplementationType { get; } - IReadOnlyList InterfaceTypes { get; } - Lifetime Lifetime { get; } - object SpawnInstance(IObjectResolver resolver); - } - - public sealed class Registration : IRegistration + sealed class Registration : IRegistration { public Type ImplementationType { get; } public IReadOnlyList InterfaceTypes { get; } @@ -54,7 +45,7 @@ public object SpawnInstance(IObjectResolver resolver) } } - public sealed class CollectionRegistration : IRegistration, IEnumerable + sealed class CollectionRegistration : IRegistration, IEnumerable { public Type ImplementationType { get; } public IReadOnlyList InterfaceTypes => interfaceTypes; @@ -112,7 +103,7 @@ public object SpawnInstance(IObjectResolver resolver) IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public sealed class ContainerRegistration : IRegistration + sealed class ContainerRegistration : IRegistration { public static readonly ContainerRegistration Default = new ContainerRegistration(); @@ -121,4 +112,4 @@ public sealed class ContainerRegistration : IRegistration public Lifetime Lifetime => Lifetime.Transient; public object SpawnInstance(IObjectResolver resolver) => resolver; } -} +} \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Internal/Registration.cs.meta b/VContainer/Assets/VContainer/Runtime/Internal/Registration.cs.meta new file mode 100644 index 00000000..154a57fb --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Internal/Registration.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 461e5e326abb400eb12109bba9e795c9 +timeCreated: 1602689096 \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Internal/TypeAnalyzer.cs b/VContainer/Assets/VContainer/Runtime/Internal/TypeAnalyzer.cs index cbef73e2..a507e1fa 100644 --- a/VContainer/Assets/VContainer/Runtime/Internal/TypeAnalyzer.cs +++ b/VContainer/Assets/VContainer/Runtime/Internal/TypeAnalyzer.cs @@ -99,7 +99,8 @@ public static InjectTypeInfo Analyze(Type type) // Constructor, single [Inject] constructor -> single most parameters constuctor var injectConstructorCount = 0; var maxParameters = -1; - foreach (var constructorInfo in typeInfo.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { + foreach (var constructorInfo in typeInfo.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { if (constructorInfo.IsDefined(typeof(InjectAttribute), true)) { if (++injectConstructorCount > 1) diff --git a/VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs b/VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs deleted file mode 100644 index 5348e6d7..00000000 --- a/VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace VContainer.Unity -{ - public interface IFactory - { - TValue Create(); - } - - public interface IFactory - { - TValue Create(TParam1 param); - } - - public interface IFactory - { - TValue Create(TParam1 param1, TParam2 param2); - } - - public interface IFactory - { - TValue Create(TParam1 param1, TParam2 param2, TParam3 param3); - } - - public interface IFactory - { - TValue Create(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4); - } -} diff --git a/VContainer/Assets/VContainer/Tests/FactoryTest.cs b/VContainer/Assets/VContainer/Tests/FactoryTest.cs new file mode 100644 index 00000000..28394394 --- /dev/null +++ b/VContainer/Assets/VContainer/Tests/FactoryTest.cs @@ -0,0 +1,328 @@ +using System; +using NUnit.Framework; + +namespace VContainer.Tests +{ + class Foo + { + public int Param1 { get; set; } + public int Param2 { get; set; } + public int Param3 { get; set; } + public int Param4 { get; set; } + public I2 Service2 { get; set; } + public I3 Service3 { get; set; } + } + + [TestFixture] + public class FactoryTest + { + [Test] + public void RegisterFactoryWithParams() + { + var builder = new ContainerBuilder(); + + builder.RegisterFactory(() => new Foo()); + builder.RegisterFactory(param1 => new Foo { Param1 = param1}); + builder.RegisterFactory((param1, param2) => new Foo + { + Param1 = param1, + Param2 = param2 + }); + builder.RegisterFactory((param1, param2, param3) => new Foo + { + Param1 = param1, + Param2 = param2, + Param3 = param3, + }); + builder.RegisterFactory((param1, param2, param3, param4) => new Foo + { + Param1 = param1, + Param2 = param2, + Param3 = param3, + Param4 = param4, + }); + + var container = builder.Build(); + + var func0 = container.Resolve>(); + Assert.That(func0(), Is.TypeOf()); + + var func1 = container.Resolve>(); + var foo1 = func1(100); + Assert.That(func1, Is.TypeOf>()); + Assert.That(foo1.Param1, Is.EqualTo(100)); + + var func2 = container.Resolve>(); + var foo2 = func2(100, 200); + Assert.That(func2, Is.TypeOf>()); + Assert.That(foo2.Param1, Is.EqualTo(100)); + Assert.That(foo2.Param2, Is.EqualTo(200)); + + var func3 = container.Resolve>(); + var foo3 = func3(100, 200, 300); + Assert.That(func3, Is.TypeOf>()); + Assert.That(foo3.Param1, Is.EqualTo(100)); + Assert.That(foo3.Param2, Is.EqualTo(200)); + Assert.That(foo3.Param3, Is.EqualTo(300)); + + var func4 = container.Resolve>(); + var foo4 = func4(100, 200, 300, 400); + Assert.That(func4, Is.TypeOf>()); + Assert.That(foo4.Param1, Is.EqualTo(100)); + Assert.That(foo4.Param2, Is.EqualTo(200)); + Assert.That(foo4.Param3, Is.EqualTo(300)); + Assert.That(foo4.Param4, Is.EqualTo(400)); + } + + [Test] + public void RegisterFactoryWithContainerTransient() + { + var builder = new ContainerBuilder(); + + builder.Register(Lifetime.Transient); + + builder.RegisterFactory(c0 => + { + var dependency = c0.Resolve(); + return () => new Foo { Service2 = dependency }; + }, Lifetime.Transient); + + builder.RegisterFactory(c1 => + { + var dependency = c1.Resolve(); + return param1 => new Foo + { + Param1 = param1 , + Service2 = dependency + }; + }, Lifetime.Transient); + + builder.RegisterFactory(c2 => + { + var dependency = c2.Resolve(); + return (param1, param2) => new Foo + { + Param1 = param1, + Param2 = param2, + Service2 = dependency + }; + }, Lifetime.Transient); + + builder.RegisterFactory(c3 => + { + var dependency = c3.Resolve(); + return (param1, param2, param3) => new Foo + { + Param1 = param1, + Param2 = param2, + Param3 = param3, + Service2 = dependency + }; + }, Lifetime.Transient); + + builder.RegisterFactory(c4 => + { + var dependency = c4.Resolve(); + return (param1, param2, param3, param4) => new Foo + { + Param1 = param1, + Param2 = param2, + Param3 = param3, + Param4 = param4, + Service2 = dependency + }; + }, Lifetime.Transient); + + var container = builder.Build(); + + var func0A = container.Resolve>(); + var func0B = container.Resolve>(); + var foo0A = func0A(); + var foo0B = func0B(); + + Assert.That(func0A, Is.TypeOf>()); + Assert.That(foo0A, Is.TypeOf()); + Assert.That(foo0A.Service2, Is.InstanceOf()); + Assert.That(foo0A.Service2, Is.Not.EqualTo(foo0B.Service2)); + + var func1A = container.Resolve>(); + var func1B = container.Resolve>(); + var foo1A = func1A(100); + var foo1B = func1B(100); + + Assert.That(func1A, Is.TypeOf>()); + Assert.That(foo1A, Is.TypeOf()); + Assert.That(foo1A.Service2, Is.InstanceOf()); + Assert.That(foo1A.Service2, Is.Not.EqualTo(foo1B.Service2)); + Assert.That(foo1A.Param1, Is.EqualTo(100)); + + var func2A = container.Resolve>(); + var func2B = container.Resolve>(); + var foo2A = func2A(100, 200); + var foo2B = func2B(100, 200); + + Assert.That(func2A, Is.TypeOf>()); + Assert.That(foo2A, Is.TypeOf()); + Assert.That(foo2A.Service2, Is.InstanceOf()); + Assert.That(foo2A.Service2, Is.Not.EqualTo(foo2B.Service2)); + Assert.That(foo2A.Param1, Is.EqualTo(100)); + Assert.That(foo2A.Param2, Is.EqualTo(200)); + + var func3A = container.Resolve>(); + var func3B = container.Resolve>(); + var foo3A = func3A(100, 200, 300); + var foo3B = func3B(100, 200, 300); + + Assert.That(func3A, Is.TypeOf>()); + Assert.That(foo3A, Is.TypeOf()); + Assert.That(foo3A.Service2, Is.InstanceOf()); + Assert.That(foo3A.Service2, Is.Not.EqualTo(foo3B.Service2)); + Assert.That(foo3A.Param1, Is.EqualTo(100)); + Assert.That(foo3A.Param2, Is.EqualTo(200)); + Assert.That(foo3A.Param3, Is.EqualTo(300)); + + var func4A = container.Resolve>(); + var func4B = container.Resolve>(); + var foo4A = func4A(100, 200, 300, 400); + var foo4B = func4B(100, 200, 300, 400); + + Assert.That(func4A, Is.TypeOf>()); + Assert.That(foo4A, Is.TypeOf()); + Assert.That(foo4A.Service2, Is.InstanceOf()); + Assert.That(foo4A.Service2, Is.Not.EqualTo(foo4B.Service2)); + Assert.That(foo4A.Param1, Is.EqualTo(100)); + Assert.That(foo4A.Param2, Is.EqualTo(200)); + Assert.That(foo4A.Param3, Is.EqualTo(300)); + Assert.That(foo4A.Param4, Is.EqualTo(400)); + } + + [Test] + public void RegisterFactoryWithContainerScoped() + { + var builder = new ContainerBuilder(); + builder.Register(Lifetime.Transient); + + builder.RegisterFactory(c0 => + { + var dependency = c0.Resolve(); + return () => new Foo { Service2 = dependency }; + }, Lifetime.Scoped); + + builder.RegisterFactory(c1 => + { + var dependency = c1.Resolve(); + return param1 => new Foo { Service2 = dependency, Param1 = param1 }; + }, Lifetime.Scoped); + + builder.RegisterFactory(c2 => + { + var dependency = c2.Resolve(); + return (param1, param2) => new Foo + { + Service2 = dependency, + Param1 = param1, + Param2 = param2 + }; + }, Lifetime.Scoped); + + builder.RegisterFactory(c3 => + { + var dependency = c3.Resolve(); + return (param1, param2) => new Foo + { + Service2 = dependency, + Param1 = param1, + Param2 = param2 + }; + }, Lifetime.Scoped); + + builder.RegisterFactory(c4 => + { + var dependency = c4.Resolve(); + return (param1, param2, param3) => new Foo + { + Service2 = dependency, + Param1 = param1, + Param2 = param2, + Param3 = param3 + }; + }, Lifetime.Scoped); + + builder.RegisterFactory(c5 => + { + var dependency = c5.Resolve(); + return (param1, param2, param3, param4) => new Foo + { + Service2 = dependency, + Param1 = param1, + Param2 = param2, + Param3 = param3, + Param4 = param4 + }; + }, Lifetime.Scoped); + + var container = builder.Build(); + + var func0A = container.Resolve>(); + var func0B = container.Resolve>(); + var foo0A = func0A(); + var foo0B = func0B(); + + Assert.That(func0A, Is.TypeOf>()); + Assert.That(foo0A, Is.TypeOf()); + Assert.That(foo0A.Service2, Is.InstanceOf()); + Assert.That(foo0A.Service2, Is.EqualTo(foo0B.Service2)); + + var func1A = container.Resolve>(); + var func1B = container.Resolve>(); + var foo1A = func1A(100); + var foo1B = func1B(100); + + Assert.That(func1A, Is.TypeOf>()); + Assert.That(foo1A, Is.TypeOf()); + Assert.That(foo1A.Service2, Is.InstanceOf()); + Assert.That(foo1A.Service2, Is.EqualTo(foo1B.Service2)); + Assert.That(foo1A.Param1, Is.EqualTo(100)); + + var func2A = container.Resolve>(); + var func2B = container.Resolve>(); + var foo2A = func2A(100, 200); + var foo2B = func2B(100, 200); + + Assert.That(func2A, Is.TypeOf>()); + Assert.That(foo2A, Is.TypeOf()); + Assert.That(foo2A.Service2, Is.InstanceOf()); + Assert.That(foo2A.Service2, Is.EqualTo(foo2B.Service2)); + Assert.That(foo2A.Param1, Is.EqualTo(100)); + Assert.That(foo2A.Param2, Is.EqualTo(200)); + + var func3A = container.Resolve>(); + var func3B = container.Resolve>(); + var foo3A = func3A(100, 200, 300); + var foo3B = func3B(100, 200, 300); + + Assert.That(func3A, Is.TypeOf>()); + Assert.That(foo3A, Is.TypeOf()); + Assert.That(foo3A.Service2, Is.InstanceOf()); + Assert.That(foo3A.Service2, Is.EqualTo(foo3B.Service2)); + Assert.That(foo3A.Param1, Is.EqualTo(100)); + Assert.That(foo3A.Param2, Is.EqualTo(200)); + Assert.That(foo3A.Param3, Is.EqualTo(300)); + + var func4A = container.Resolve>(); + var func4B = container.Resolve>(); + var foo4A = func4A(100, 200, 300, 400); + var foo4B = func4B(100, 200, 300, 400); + + Assert.That(func4A, Is.TypeOf>()); + Assert.That(foo4A, Is.TypeOf()); + Assert.That(foo4A.Service2, Is.InstanceOf()); + Assert.That(foo4A.Service2, Is.EqualTo(foo4B.Service2)); + Assert.That(foo4A.Param1, Is.EqualTo(100)); + Assert.That(foo4A.Param2, Is.EqualTo(200)); + Assert.That(foo4A.Param3, Is.EqualTo(300)); + Assert.That(foo4A.Param4, Is.EqualTo(400)); + + } + } +} \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs.meta b/VContainer/Assets/VContainer/Tests/FactoryTest.cs.meta similarity index 83% rename from VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs.meta rename to VContainer/Assets/VContainer/Tests/FactoryTest.cs.meta index 80cb2e6c..7ba90dad 100644 --- a/VContainer/Assets/VContainer/Runtime/Unity/IFactory.cs.meta +++ b/VContainer/Assets/VContainer/Tests/FactoryTest.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f9b90a75e0e244579a93c963ea45cd86 +guid: a1ee44a97e1ac4d81a5d844696f701ac MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VContainer/VContainer.csproj b/VContainer/VContainer.csproj index 4bad41cb..c07f62b1 100644 --- a/VContainer/VContainer.csproj +++ b/VContainer/VContainer.csproj @@ -25,7 +25,7 @@ full false Temp\bin\Debug\ - DEBUG;TRACE;UNITY_2019_4_5;UNITY_2019_4;UNITY_2019;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2019_1_OR_NEWER;UNITY_2019_2_OR_NEWER;UNITY_2019_3_OR_NEWER;UNITY_2019_4_OR_NEWER;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AR;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_TEXTURE_STREAMING;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_VR;ENABLE_WEBCAM;ENABLE_UNITYWEBREQUEST;ENABLE_WWW;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_NATIVE_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;INCLUDE_DYNAMIC_GI;ENABLE_MONO_BDWGC;ENABLE_SCRIPTING_GC_WBARRIERS;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;ENABLE_VIDEO;PLATFORM_STANDALONE;PLATFORM_STANDALONE_OSX;UNITY_STANDALONE_OSX;UNITY_STANDALONE;ENABLE_GAMECENTER;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_SPATIALTRACKING;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_STANDARD_2_0;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_OSX;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_DIRECTOR;ENABLE_LOCALIZATION;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_TILEMAP;ENABLE_TIMELINE;ENABLE_LEGACY_INPUT_MANAGER;VCONTAINER_ECS_INTEGRATION;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER + DEBUG;TRACE;UNITY_2019_4_5;UNITY_2019_4;UNITY_2019;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2019_1_OR_NEWER;UNITY_2019_2_OR_NEWER;UNITY_2019_3_OR_NEWER;UNITY_2019_4_OR_NEWER;UNITY_INCLUDE_TESTS;ENABLE_AR;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_TEXTURE_STREAMING;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_VR;ENABLE_WEBCAM;ENABLE_UNITYWEBREQUEST;ENABLE_WWW;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_ENGINE_CODE_STRIPPING;ENABLE_ONSCREEN_KEYBOARD;INCLUDE_DYNAMIC_GI;ENABLE_MONO_BDWGC;ENABLE_SCRIPTING_GC_WBARRIERS;PLATFORM_SUPPORTS_MONO;ENABLE_VIDEO;PLATFORM_IOS;ENABLE_RUNTIME_GI;ENABLE_GAMECENTER;ENABLE_NETWORK;ENABLE_IOS_ON_DEMAND_RESOURCES;ENABLE_IOS_APP_SLICING;PLAYERCONNECTION_LISTENS_FIXED_PORT;DEBUGGER_LISTENS_FIXED_PORT;PLATFORM_SUPPORTS_ADS_ID;SUPPORT_ENVIRONMENT_VARIABLES;PLATFORM_SUPPORTS_PROFILER;ENABLE_UNITYADS_RUNTIME;UNITY_UNITYADS_API;UNITY_IOS;PLATFORM_IPHONE;UNITY_IPHONE;UNITY_IPHONE_API;SUPPORT_MULTIPLE_DISPLAYS;UNITY_HAS_GOOGLEVR;ENABLE_SPATIALTRACKING;ENABLE_MONO;NET_STANDARD_2_0;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_OSX;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_DIRECTOR;ENABLE_LOCALIZATION;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_TILEMAP;ENABLE_TIMELINE;ENABLE_LEGACY_INPUT_MANAGER;VCONTAINER_ECS_INTEGRATION;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER prompt 4 0169 @@ -65,9 +65,11 @@ + + @@ -76,15 +78,14 @@ + - - @@ -93,6 +94,7 @@ + @@ -107,20 +109,29 @@ /Users/hadashi/dev/VContainer/VContainer/Library/ScriptAssemblies/UnityEngine.UI.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AIModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ARModule.dll /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AccessibilityModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AndroidJNIModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AnimationModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AssetBundleModule.dll - - /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ClusterInputModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.AudioModule.dll - - /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ClusterRendererModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ClothModule.dll /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.CoreModule.dll @@ -131,6 +142,9 @@ /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.DSPGraphModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.DirectorModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.GameCenterModule.dll @@ -143,6 +157,9 @@ /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.IMGUIModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ImageConversionModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.InputModule.dll @@ -155,12 +172,24 @@ /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.LocalizationModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ParticleSystemModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.PerformanceReportingModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.PhysicsModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.Physics2DModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ProfilerModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.ScreenCaptureModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.SharedInternalsModule.dll @@ -176,15 +205,27 @@ /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.SubstanceModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.SubsystemsModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TLSModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TerrainModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TerrainPhysicsModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TextCoreModule.dll /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TextRenderingModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.TilemapModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UIModule.dll @@ -194,15 +235,51 @@ /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UNETModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UmbraModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityAnalyticsModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityConnectModule.dll /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityTestProtocolModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityWebRequestModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityWebRequestAssetBundleModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityWebRequestAudioModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityWebRequestTextureModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.UnityWebRequestWWWModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.VFXModule.dll + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.VRModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.VehiclesModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.VideoModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.WindModule.dll + + + /Applications/Unity/2019.4.5f1/Unity.app/Contents/Managed/UnityEngine/UnityEngine.XRModule.dll + /Applications/Unity/2019.4.5f1/Unity.app/Contents/UnityExtensions/Unity/UnityVR/Editor/UnityEditor.VR.dll