Initial Commit
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class ActionInstaller : Installer<ActionInstaller>
|
||||
{
|
||||
readonly Action<DiContainer> _installMethod;
|
||||
|
||||
public ActionInstaller(Action<DiContainer> installMethod)
|
||||
{
|
||||
_installMethod = installMethod;
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
_installMethod(Container);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e182a0b67fa936e40bebc0dc2f28743a
|
||||
timeCreated: 1476911606
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,438 @@
|
||||
using System.Linq;
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 219
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class CheatSheet : Installer<CheatSheet>
|
||||
{
|
||||
public override void InstallBindings()
|
||||
{
|
||||
// Create a new instance of Foo for every class that asks for it
|
||||
Container.Bind<Foo>().AsTransient();
|
||||
|
||||
// Create a new instance of Foo for every class that asks for an IFoo
|
||||
Container.Bind<IFoo>().To<Foo>().AsTransient();
|
||||
|
||||
// Non generic version of the above
|
||||
Container.Bind(typeof(IFoo)).To(typeof(Foo)).AsTransient();
|
||||
|
||||
///////////// AsSingle
|
||||
|
||||
// Create one definitive instance of Foo and re-use that for every class that asks for it
|
||||
Container.Bind<Foo>().AsSingle();
|
||||
|
||||
// Create one definitive instance of Foo and re-use that for every class that asks for IFoo
|
||||
Container.Bind<IFoo>().To<Foo>().AsSingle();
|
||||
|
||||
// Bind the same instance to multiple types
|
||||
// In this example, the same instance of Foo will be used for all three types
|
||||
// (we have to use the non-generic version of Bind when mapping to multiple types)
|
||||
Container.Bind(typeof(Foo), typeof(IFoo), typeof(IFoo2)).To<Foo>().AsSingle();
|
||||
|
||||
///////////// BindInterfaces
|
||||
|
||||
// This will have the exact same effect as the above line
|
||||
// Bind all interfaces that Foo implements and Foo itself to a new singleton of type Foo
|
||||
Container.BindInterfacesAndSelfTo<Foo>().AsSingle();
|
||||
|
||||
// Bind only the interfaces that Foo implements to an instance of Foo
|
||||
// This can be useful if you don't want any classes to directly reference the concrete
|
||||
// derived type
|
||||
Container.BindInterfacesTo<Foo>().AsSingle();
|
||||
|
||||
///////////// FromInstance
|
||||
|
||||
// Use the given instance everywhere that Foo is used
|
||||
// Note that in this case there's no good reason to use FromInstance
|
||||
Container.Bind<Foo>().FromInstance(new Foo());
|
||||
|
||||
// This is simply a shortcut for the above binding
|
||||
// This can be a bit nicer since the type argument can be deduced from the parameter
|
||||
Container.BindInstance(new Foo());
|
||||
|
||||
// Bind multiple instances at once
|
||||
Container.BindInstances(new Foo(), new Bar());
|
||||
|
||||
///////////// Binding primitive types
|
||||
|
||||
// BindInstance is more commonly used with primitive types
|
||||
// Use the number 10 every time an int is requested
|
||||
Container.Bind<int>().FromInstance(10);
|
||||
Container.Bind<bool>().FromInstance(false);
|
||||
|
||||
// Or equivalently:
|
||||
Container.BindInstance(10);
|
||||
Container.BindInstance(false);
|
||||
|
||||
// You'd never really want to do the above though - you should almost always use a When condition for primitive values
|
||||
Container.BindInstance(10).WhenInjectedInto<Foo>();
|
||||
|
||||
///////////// FromMethod
|
||||
|
||||
// Create instance of Foo when requested, using the given method
|
||||
// Note that for more complex construction scenarios, you might consider using a factory
|
||||
// instead with FromFactory
|
||||
Container.Bind<Foo>().FromMethod(GetFoo);
|
||||
|
||||
// Randomly return one of several different implementations of IFoo
|
||||
// We use Instantiate here instead of just new so that Foo1 gets its members injected
|
||||
Container.Bind<IFoo>().FromMethod(GetRandomFoo);
|
||||
|
||||
// You an also use an anonymouse delegate directly
|
||||
Container.Bind<Foo>().FromMethod(ctx => new Foo());
|
||||
|
||||
// This is equivalent to AsTransient
|
||||
Container.Bind<Foo>().FromMethod(ctx => ctx.Container.Instantiate<Foo>());
|
||||
|
||||
InstallMore();
|
||||
}
|
||||
|
||||
Foo GetFoo(InjectContext ctx)
|
||||
{
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
IFoo GetRandomFoo(InjectContext ctx)
|
||||
{
|
||||
switch (Random.Range(0, 3))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
return ctx.Container.Instantiate<Foo1>();
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
return ctx.Container.Instantiate<Foo2>();
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.Container.Instantiate<Foo3>();
|
||||
}
|
||||
|
||||
void InstallMore()
|
||||
{
|
||||
///////////// FromResolveGetter
|
||||
|
||||
// Bind to a property on another dependency
|
||||
// This can be helpful to reduce coupling between classes
|
||||
Container.Bind<Foo>().AsSingle();
|
||||
|
||||
Container.Bind<Bar>().FromResolveGetter<Foo>(foo => foo.GetBar());
|
||||
|
||||
// Another example using values
|
||||
Container.Bind<string>().FromResolveGetter<Foo>(foo => foo.GetTitle());
|
||||
|
||||
///////////// FromNewComponentOnNewGameObject
|
||||
|
||||
// Create a new game object at the root of the scene and add the Foo MonoBehaviour to it
|
||||
Container.Bind<Foo>().FromNewComponentOnNewGameObject().AsSingle();
|
||||
|
||||
// You can also specify the game object name to use using WithGameObjectName
|
||||
Container.Bind<Foo>().FromNewComponentOnNewGameObject().WithGameObjectName("Foo1").AsSingle();
|
||||
|
||||
// Bind to an interface instead
|
||||
Container.Bind<IFoo>().To<Foo>().FromNewComponentOnNewGameObject().AsSingle();
|
||||
|
||||
///////////// FromComponentInNewPrefab (singleton)
|
||||
|
||||
// Create a new game object at the root of the scene using the given prefab
|
||||
// After zenject creates a new GameObject from the given prefab, it will
|
||||
// search the prefab for a component of type 'Foo' and return that
|
||||
GameObject prefab = null;
|
||||
Container.Bind<Foo>().FromComponentInNewPrefab(prefab).AsSingle();
|
||||
|
||||
// Bind to interface instead
|
||||
Container.Bind<IFoo>().To<Foo>().FromComponentInNewPrefab(prefab).AsSingle();
|
||||
|
||||
// You can also add multiple components
|
||||
// Note here that only one instance of the given prefab will be
|
||||
// created
|
||||
// For this to work, there must be both a Foo MonoBehaviour and
|
||||
// a Bar MonoBehaviour somewhere on the prefab
|
||||
Container.Bind(typeof(Foo), typeof(Bar)).FromComponentInNewPrefab(prefab).AsSingle();
|
||||
|
||||
///////////// FromComponentInNewPrefab (Transient)
|
||||
|
||||
// Instantiate a new copy of 'prefab' every time an instance of Foo is
|
||||
// requested by a constructor parameter, injected field, etc.
|
||||
Container.Bind<Foo>().FromComponentInNewPrefab(prefab).AsTransient();
|
||||
|
||||
// Bind to interface instead
|
||||
Container.Bind<IFoo>().To<Foo>().FromComponentInNewPrefab(prefab);
|
||||
|
||||
///////////// Identifiers
|
||||
|
||||
// Bind a globally accessible string with the name 'PlayerName'
|
||||
// Note however that a better option might be to create a Settings object and bind
|
||||
// that instead
|
||||
Container.Bind<string>().WithId("PlayerName").FromInstance("name of the player");
|
||||
|
||||
// This is the equivalent of the line above, and is a bit more readable
|
||||
Container.BindInstance("name of the player").WithId("PlayerName");
|
||||
|
||||
// We can also use IDs to bind multiple instances of the same type:
|
||||
Container.BindInstance("foo").WithId("FooA");
|
||||
Container.BindInstance("asdf").WithId("FooB");
|
||||
|
||||
InstallMore2();
|
||||
}
|
||||
|
||||
// Then when we inject these dependencies we have to use the same ID:
|
||||
public class Norf
|
||||
{
|
||||
[Inject(Id = "FooA")]
|
||||
public string Foo;
|
||||
}
|
||||
|
||||
public class Qux
|
||||
{
|
||||
[Inject(Id = "FooB")]
|
||||
public string Foo;
|
||||
}
|
||||
|
||||
public void InstallMore2()
|
||||
{
|
||||
///////////// AsCached
|
||||
|
||||
// In this example, we bind three instances of Foo, including one without an ID
|
||||
// We have to use AsCached here because Foo is not a singleton, but we also
|
||||
// do not want a new Foo created every time like AsTransient
|
||||
// This will result in a maximum of 3 instances of Foo
|
||||
Container.Bind<Foo>().AsCached();
|
||||
Container.Bind<Foo>().WithId("FooA").AsCached();
|
||||
Container.Bind<Foo>().WithId("FooA").AsCached();
|
||||
|
||||
InstallMore3();
|
||||
}
|
||||
|
||||
// When an ID is unspecified in an [Inject] field, it will use the first
|
||||
// instance
|
||||
// Bindings without IDs can therefore be used as a default and we can
|
||||
// specify IDs for specific versions of the same type
|
||||
public class Norf2
|
||||
{
|
||||
[Inject]
|
||||
public Foo Foo;
|
||||
}
|
||||
|
||||
// Qux2._foo will be the same instance as Norf2._foo
|
||||
// This is because we are using AsCached rather than AsTransient
|
||||
public class Qux2
|
||||
{
|
||||
[Inject]
|
||||
public Foo Foo;
|
||||
|
||||
[Inject(Id = "FooA")]
|
||||
public Foo Foo2;
|
||||
}
|
||||
|
||||
public void InstallMore3()
|
||||
{
|
||||
///////////// Conditions
|
||||
|
||||
// This will make Foo only visible to Bar
|
||||
// If we add Foo to the constructor of any other class it won't find it
|
||||
Container.Bind<Foo>().AsSingle().WhenInjectedInto<Bar>();
|
||||
|
||||
// Use different implementations of IFoo dependending on which
|
||||
// class is being injected
|
||||
Container.Bind<IFoo>().To<Foo1>().AsSingle().WhenInjectedInto<Bar>();
|
||||
Container.Bind<IFoo>().To<Foo2>().AsSingle().WhenInjectedInto<Qux>();
|
||||
|
||||
// Use "Foo1" as the default implementation except when injecting into
|
||||
// class Qux, in which case use Foo2
|
||||
// This works because if there is a condition match, that takes precedence
|
||||
Container.Bind<IFoo>().To<Foo1>().AsSingle();
|
||||
Container.Bind<IFoo>().To<Foo2>().AsSingle().WhenInjectedInto<Qux>();
|
||||
|
||||
// Allow depending on Foo in only a few select classes
|
||||
Container.Bind<Foo>().AsSingle().WhenInjectedInto(typeof(Bar), typeof(Qux), typeof(Baz));
|
||||
|
||||
// Supply "my game" for any strings that are injected into the Gui class with the identifier "Title"
|
||||
Container.BindInstance("my game").WithId("Title").WhenInjectedInto<Gui>();
|
||||
|
||||
// Supply 5 for all ints that are injected into the Gui class
|
||||
Container.BindInstance(5).WhenInjectedInto<Gui>();
|
||||
|
||||
// Supply 5 for all ints that are injected into a parameter or field
|
||||
// inside type Gui that is named 'width'
|
||||
// Note that this is usually not a good idea since the name of a field can change
|
||||
// easily and break the binding but shown here as an example of a more complex
|
||||
// condition
|
||||
Container.BindInstance(5.0f).When(ctx =>
|
||||
ctx.ObjectType == typeof(Gui) && ctx.MemberName == "width");
|
||||
|
||||
// Create a new 'Foo' for every class that is created as part of the
|
||||
// construction of the 'Bar' class
|
||||
// So if Bar has a constructor parameter of type Qux, and Qux has
|
||||
// a constructor parameter of type IFoo, a new Foo will be created
|
||||
// for that case
|
||||
Container.Bind<IFoo>().To<Foo>().AsTransient().When(
|
||||
ctx => ctx.AllObjectTypes.Contains(typeof(Bar)));
|
||||
|
||||
///////////// Complex conditions example
|
||||
|
||||
var foo1 = new Foo();
|
||||
var foo2 = new Foo();
|
||||
|
||||
Container.Bind<Bar>().WithId("Bar1").AsCached();
|
||||
Container.Bind<Bar>().WithId("Bar2").AsCached();
|
||||
|
||||
// Here we use the 'ParentContexts' property of inject context to sync multiple corresponding identifiers
|
||||
Container.BindInstance(foo1).When(c => c.ParentContexts.Where(x => x.MemberType == typeof(Bar) && Equals(x.Identifier, "Bar1")).Any());
|
||||
Container.BindInstance(foo2).When(c => c.ParentContexts.Where(x => x.MemberType == typeof(Bar) && Equals(x.Identifier, "Bar2")).Any());
|
||||
|
||||
// This results in:
|
||||
Assert.That(Container.ResolveId<Bar>("Bar1").Foo == foo1);
|
||||
Assert.That(Container.ResolveId<Bar>("Bar2").Foo == foo2);
|
||||
|
||||
///////////// FromResolve
|
||||
|
||||
// FromResolve does another lookup on the container
|
||||
// This will result in IBar, IFoo, and Foo, all being bound to the same instance of
|
||||
// Foo which is assume to exist somewhere on the given prefab
|
||||
GameObject fooPrefab = null;
|
||||
Container.Bind<Foo>().FromComponentInNewPrefab(fooPrefab).AsSingle();
|
||||
Container.Bind<IBar>().To<Foo>().FromResolve();
|
||||
Container.Bind<IFoo>().To<IBar>().FromResolve();
|
||||
|
||||
// This will result in the same behaviour as the above
|
||||
Container.Bind(typeof(Foo), typeof(IBar), typeof(IFoo)).To<Foo>().FromComponentInNewPrefab(fooPrefab).AsSingle();
|
||||
|
||||
InstallMore4();
|
||||
}
|
||||
|
||||
public class FooInstaller : Installer<FooInstaller>
|
||||
{
|
||||
public FooInstaller(string foo)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class FooInstallerWithArgs : Installer<string, FooInstallerWithArgs>
|
||||
{
|
||||
public FooInstallerWithArgs(string foo)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void InstallMore4()
|
||||
{
|
||||
///////////// Installing Other Installers
|
||||
|
||||
// Immediately call InstallBindings() on FooInstaller
|
||||
FooInstaller.Install(Container);
|
||||
|
||||
// Before calling FooInstaller, configure a property of it
|
||||
Container.BindInstance("foo").WhenInjectedInto<FooInstaller>();
|
||||
FooInstaller.Install(Container);
|
||||
|
||||
// The arguments can also be added to the Installer<> generic arguments to make them
|
||||
// strongly typed
|
||||
FooInstallerWithArgs.Install(Container, "foo");
|
||||
|
||||
///////////// Manual Use of Container
|
||||
|
||||
// This will fill in any parameters marked as [Inject] and also call any [Inject] methods
|
||||
var foo = new Foo();
|
||||
Container.Inject(foo);
|
||||
|
||||
// Return an instance for IFoo, using the bindings that have been added previously
|
||||
// Internally it is what is triggered when you fill in a constructor parameter of type IFoo
|
||||
// Note: It will throw an exception if it cannot find a match
|
||||
Container.Resolve<IFoo>();
|
||||
|
||||
// Same as the above except returns null when it can't find the given type
|
||||
Container.TryResolve<IFoo>();
|
||||
|
||||
// Return a list of 2 instances of type Foo
|
||||
// Note that in this case simply calling Resolve<IFoo> will trigger an exception
|
||||
Container.BindInstance(new Foo());
|
||||
Container.BindInstance(new Foo());
|
||||
var foos = Container.ResolveAll<IFoo>();
|
||||
|
||||
// Create a new instance of Foo and inject on any of its members
|
||||
// And fill in any constructor parameters Foo might have
|
||||
Container.Instantiate<Foo>();
|
||||
|
||||
GameObject prefab1 = null;
|
||||
GameObject prefab2 = null;
|
||||
|
||||
// Instantiate a new prefab and have any injectables filled in on the prefab
|
||||
GameObject go = Container.InstantiatePrefab(prefab1);
|
||||
|
||||
// Instantiate a new prefab and return a specific monobehaviour
|
||||
Foo foo2 = Container.InstantiatePrefabForComponent<Foo>(prefab2);
|
||||
|
||||
// Add a new component to an existing game object
|
||||
Foo foo3 = Container.InstantiateComponent<Foo>(go);
|
||||
}
|
||||
|
||||
public interface IFoo2
|
||||
{
|
||||
}
|
||||
|
||||
public interface IFoo
|
||||
{
|
||||
}
|
||||
|
||||
public interface IBar : IFoo
|
||||
{
|
||||
}
|
||||
|
||||
public class Foo : MonoBehaviour, IFoo, IFoo2, IBar
|
||||
{
|
||||
public Bar GetBar()
|
||||
{
|
||||
return new Bar();
|
||||
}
|
||||
|
||||
public string GetTitle()
|
||||
{
|
||||
return "title";
|
||||
}
|
||||
}
|
||||
|
||||
public class Foo1 : IFoo
|
||||
{
|
||||
}
|
||||
|
||||
public class Foo2 : IFoo
|
||||
{
|
||||
}
|
||||
|
||||
public class Foo3 : IFoo
|
||||
{
|
||||
}
|
||||
|
||||
public class Baz
|
||||
{
|
||||
}
|
||||
|
||||
public class Gui
|
||||
{
|
||||
}
|
||||
|
||||
public class Bar : IBar
|
||||
{
|
||||
public Foo Foo
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2fc6db101e75248ab98ad463a99ffa
|
||||
timeCreated: 1528895686
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
#if !NOT_UNITY3D
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class DefaultGameObjectParentInstaller : Installer<string, DefaultGameObjectParentInstaller>
|
||||
{
|
||||
readonly string _name;
|
||||
|
||||
public DefaultGameObjectParentInstaller(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
#if !ZEN_TESTS_OUTSIDE_UNITY
|
||||
var defaultParent = new GameObject(_name);
|
||||
|
||||
defaultParent.transform.SetParent(
|
||||
Container.InheritedDefaultParent, false);
|
||||
|
||||
Container.DefaultParent = defaultParent.transform;
|
||||
|
||||
Container.Bind<IDisposable>()
|
||||
.To<DefaultParentObjectDestroyer>().AsCached().WithArguments(defaultParent);
|
||||
|
||||
// Always destroy the default parent last so that the non-monobehaviours get a chance
|
||||
// to clean it up if they want to first
|
||||
Container.BindDisposableExecutionOrder<DefaultParentObjectDestroyer>(int.MinValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
class DefaultParentObjectDestroyer : IDisposable
|
||||
{
|
||||
readonly GameObject _gameObject;
|
||||
|
||||
public DefaultParentObjectDestroyer(GameObject gameObject)
|
||||
{
|
||||
_gameObject = gameObject;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GameObject.Destroy(_gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd79d0a4f0b28314cbd6701ff5ab9062
|
||||
timeCreated: 1538629352
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ModestTree;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
[NoReflectionBaking]
|
||||
public class DisposeBlock : IDisposable
|
||||
{
|
||||
static readonly StaticMemoryPool<DisposeBlock> _pool =
|
||||
new StaticMemoryPool<DisposeBlock>(OnSpawned, OnDespawned);
|
||||
|
||||
List<IDisposable> _disposables;
|
||||
List<SpawnedObjectPoolPair> _objectPoolPairs;
|
||||
|
||||
static void OnSpawned(DisposeBlock that)
|
||||
{
|
||||
Assert.IsNull(that._disposables);
|
||||
Assert.IsNull(that._objectPoolPairs);
|
||||
}
|
||||
|
||||
static void OnDespawned(DisposeBlock that)
|
||||
{
|
||||
if (that._disposables != null)
|
||||
{
|
||||
// Dispose in reverse order since usually that makes the most sense
|
||||
for (int i = that._disposables.Count - 1; i >= 0; i--)
|
||||
{
|
||||
that._disposables[i].Dispose();
|
||||
}
|
||||
ListPool<IDisposable>.Instance.Despawn(that._disposables);
|
||||
that._disposables = null;
|
||||
}
|
||||
|
||||
if (that._objectPoolPairs != null)
|
||||
{
|
||||
// Dispose in reverse order since usually that makes the most sense
|
||||
for (int i = that._objectPoolPairs.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var pair = that._objectPoolPairs[i];
|
||||
pair.Pool.Despawn(pair.Object);
|
||||
}
|
||||
ListPool<SpawnedObjectPoolPair>.Instance.Despawn(that._objectPoolPairs);
|
||||
that._objectPoolPairs = null;
|
||||
}
|
||||
}
|
||||
|
||||
void LazyInitializeDisposableList()
|
||||
{
|
||||
if (_disposables == null)
|
||||
{
|
||||
_disposables = ListPool<IDisposable>.Instance.Spawn();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange<T>(IList<T> disposables)
|
||||
where T : IDisposable
|
||||
{
|
||||
LazyInitializeDisposableList();
|
||||
for (int i = 0; i < disposables.Count; i++)
|
||||
{
|
||||
_disposables.Add(disposables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(IDisposable disposable)
|
||||
{
|
||||
LazyInitializeDisposableList();
|
||||
Assert.That(!_disposables.Contains(disposable));
|
||||
_disposables.Add(disposable);
|
||||
}
|
||||
|
||||
public void Remove(IDisposable disposable)
|
||||
{
|
||||
Assert.IsNotNull(_disposables);
|
||||
_disposables.RemoveWithConfirm(disposable);
|
||||
}
|
||||
|
||||
void StoreSpawnedObject<T>(T obj, IDespawnableMemoryPool<T> pool)
|
||||
{
|
||||
if (typeof(T).DerivesFrom<IDisposable>())
|
||||
{
|
||||
Add((IDisposable)obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This allocation is ok because it's a struct
|
||||
var pair = new SpawnedObjectPoolPair
|
||||
{
|
||||
Pool = pool,
|
||||
Object = obj
|
||||
};
|
||||
|
||||
if (_objectPoolPairs == null)
|
||||
{
|
||||
_objectPoolPairs = ListPool<SpawnedObjectPoolPair>.Instance.Spawn();
|
||||
}
|
||||
_objectPoolPairs.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
public T Spawn<T>(IMemoryPool<T> pool)
|
||||
{
|
||||
var obj = pool.Spawn();
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1>(IMemoryPool<TParam1, TValue> pool, TParam1 p1)
|
||||
{
|
||||
var obj = pool.Spawn(p1);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2>(IMemoryPool<TParam1, TParam2, TValue> pool, TParam1 p1, TParam2 p2)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2, TParam3>(IMemoryPool<TParam1, TParam2, TParam3, TValue> pool, TParam1 p1, TParam2 p2, TParam3 p3)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2, p3);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2, TParam3, TParam4>(IMemoryPool<TParam1, TParam2, TParam3, TParam4, TValue> pool, TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2, p3, p4);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2, TParam3, TParam4, TParam5>(IMemoryPool<TParam1, TParam2, TParam3, TParam4, TParam5, TValue> pool, TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2, p3, p4, p5);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2, TParam3, TParam4, TParam5, TParam6>(IMemoryPool<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TValue> pool, TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5, TParam6 p6)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2, p3, p4, p5, p6);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public TValue Spawn<TValue, TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7>(IMemoryPool<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TValue> pool, TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5, TParam6 p6, TParam7 p7)
|
||||
{
|
||||
var obj = pool.Spawn(p1, p2, p3, p4, p5, p6, p7);
|
||||
StoreSpawnedObject(obj, pool);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public List<T> SpawnList<T>(IEnumerable<T> elements)
|
||||
{
|
||||
var list = SpawnList<T>();
|
||||
list.AddRange(elements);
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<T> SpawnList<T>()
|
||||
{
|
||||
return Spawn(ListPool<T>.Instance);
|
||||
}
|
||||
|
||||
public static DisposeBlock Spawn()
|
||||
{
|
||||
return _pool.Spawn();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
|
||||
struct SpawnedObjectPoolPair
|
||||
{
|
||||
public IMemoryPool Pool;
|
||||
public object Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 031fb76cf466ade4baf3269c39c146bd
|
||||
timeCreated: 1519832826
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class ExecutionOrderInstaller : Installer<List<Type>, ExecutionOrderInstaller>
|
||||
{
|
||||
List<Type> _typeOrder;
|
||||
|
||||
public ExecutionOrderInstaller(List<Type> typeOrder)
|
||||
{
|
||||
_typeOrder = typeOrder;
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
// All tickables without explicit priorities assigned are given order of zero,
|
||||
// so put all of these before that (ie. negative)
|
||||
int order = -1 * _typeOrder.Count;
|
||||
|
||||
foreach (var type in _typeOrder)
|
||||
{
|
||||
Container.BindExecutionOrder(type, order);
|
||||
order++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cad41a65c3c0b0f46a659557b2d716e0
|
||||
timeCreated: 1461708053
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using ModestTree;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine.Profiling;
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
[NoReflectionBaking]
|
||||
public class ProfileBlock : IDisposable
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static int _blockCount;
|
||||
static ProfileBlock _instance = new ProfileBlock();
|
||||
static Dictionary<int, string> _nameCache = new Dictionary<int, string>();
|
||||
|
||||
ProfileBlock()
|
||||
{
|
||||
}
|
||||
|
||||
public static Thread UnityMainThread
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public static Regex ProfilePattern
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
static int GetHashCode(object p1, object p2)
|
||||
{
|
||||
unchecked // Overflow is fine, just wrap
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 29 + p1.GetHashCode();
|
||||
hash = hash * 29 + p2.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetHashCode(object p1, object p2, object p3)
|
||||
{
|
||||
unchecked // Overflow is fine, just wrap
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 29 + p1.GetHashCode();
|
||||
hash = hash * 29 + p2.GetHashCode();
|
||||
hash = hash * 29 + p3.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static ProfileBlock Start(string sampleNameFormat, object obj1, object obj2)
|
||||
{
|
||||
#if ZEN_TESTS_OUTSIDE_UNITY
|
||||
return null;
|
||||
#else
|
||||
if (UnityMainThread == null
|
||||
|| !UnityMainThread.Equals(Thread.CurrentThread))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Profiler.enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// We need to ensure that we do not have per-frame allocations in ProfileBlock
|
||||
// to avoid infecting the test too much, so use a cache of formatted strings given
|
||||
// the input values
|
||||
// This only works if the input values do not change per frame
|
||||
var hash = GetHashCode(sampleNameFormat, obj1, obj2);
|
||||
|
||||
string formatString;
|
||||
|
||||
if (!_nameCache.TryGetValue(hash, out formatString))
|
||||
{
|
||||
formatString = string.Format(sampleNameFormat, obj1, obj2);
|
||||
_nameCache.Add(hash, formatString);
|
||||
}
|
||||
|
||||
return StartInternal(formatString);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static ProfileBlock Start(string sampleNameFormat, object obj)
|
||||
{
|
||||
#if ZEN_TESTS_OUTSIDE_UNITY
|
||||
return null;
|
||||
#else
|
||||
if (UnityMainThread == null
|
||||
|| !UnityMainThread.Equals(Thread.CurrentThread))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Profiler.enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// We need to ensure that we do not have per-frame allocations in ProfileBlock
|
||||
// to avoid infecting the test too much, so use a cache of formatted strings given
|
||||
// the input values
|
||||
// This only works if the input values do not change per frame
|
||||
var hash = GetHashCode(sampleNameFormat, obj);
|
||||
|
||||
string formatString;
|
||||
|
||||
if (!_nameCache.TryGetValue(hash, out formatString))
|
||||
{
|
||||
formatString = string.Format(sampleNameFormat, obj);
|
||||
_nameCache.Add(hash, formatString);
|
||||
}
|
||||
|
||||
return StartInternal(formatString);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static ProfileBlock Start(string sampleName)
|
||||
{
|
||||
#if ZEN_TESTS_OUTSIDE_UNITY
|
||||
return null;
|
||||
#else
|
||||
if (UnityMainThread == null
|
||||
|| !UnityMainThread.Equals(Thread.CurrentThread))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Profiler.enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return StartInternal(sampleName);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ProfileBlock StartInternal(string sampleName)
|
||||
{
|
||||
Assert.That(Profiler.enabled);
|
||||
|
||||
if (ProfilePattern == null || ProfilePattern.Match(sampleName).Success)
|
||||
{
|
||||
Profiler.BeginSample(sampleName);
|
||||
_blockCount++;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_blockCount--;
|
||||
Assert.That(_blockCount >= 0);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
#else
|
||||
ProfileBlock(string sampleName, bool rootBlock)
|
||||
{
|
||||
}
|
||||
|
||||
ProfileBlock(string sampleName)
|
||||
: this(sampleName, false)
|
||||
{
|
||||
}
|
||||
|
||||
public static Regex ProfilePattern
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public static ProfileBlock Start()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileBlock Start(string sampleNameFormat, object obj1, object obj2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the call completely for builds
|
||||
public static ProfileBlock Start(string sampleNameFormat, object obj)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the call completely for builds
|
||||
public static ProfileBlock Start(string sampleName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 952433523e6a6e445adc4ac7e2086e7d
|
||||
timeCreated: 1485104137
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ModestTree;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
// Similar to ProfileBlock except used for measuring speed of zenject specifically
|
||||
// And does not use unity's profiler
|
||||
public static class ProfileTimers
|
||||
{
|
||||
static Dictionary<string, TimerInfo> _timers = new Dictionary<string, TimerInfo>();
|
||||
|
||||
public static void ResetAll()
|
||||
{
|
||||
foreach (var timer in _timers.Values)
|
||||
{
|
||||
timer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatResults()
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
|
||||
// Uncomment if you only want to see zenject related info
|
||||
//var timers = _timers.Where(x => x.Key != "User Code");
|
||||
var timers = _timers;
|
||||
|
||||
var total = timers.Select(x => x.Value.TotalMilliseconds).Sum();
|
||||
|
||||
result.Append("Total time tracked: {0:0.00} ms. Details:".Fmt(total));
|
||||
|
||||
foreach (var pair in timers.OrderByDescending(x => x.Value.TotalMilliseconds))
|
||||
{
|
||||
var time = pair.Value.TotalMilliseconds;
|
||||
var percent = 100.0 * (time / total);
|
||||
var name = pair.Key;
|
||||
|
||||
result.Append("\n {0:00.0}% ({1:00000}x) ({2:0000} ms) {3}".Fmt(percent, pair.Value.CallCount, time, name));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static double GetTimerElapsedMilliseconds(string name)
|
||||
{
|
||||
return _timers[name].TotalMilliseconds;
|
||||
}
|
||||
|
||||
public static IDisposable CreateTimedBlock(string name)
|
||||
{
|
||||
TimerInfo timer;
|
||||
|
||||
if (!_timers.TryGetValue(name, out timer))
|
||||
{
|
||||
timer = new TimerInfo();
|
||||
_timers.Add(name, timer);
|
||||
}
|
||||
|
||||
timer.CallCount++;
|
||||
|
||||
if (timer.IsRunning)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return TimedBlock.Pool.Spawn(timer);
|
||||
}
|
||||
|
||||
class TimedBlock : IDisposable
|
||||
{
|
||||
public static StaticMemoryPool<TimerInfo, TimedBlock> Pool =
|
||||
new StaticMemoryPool<TimerInfo, TimedBlock>(OnSpawned, OnDespawned);
|
||||
|
||||
readonly List<TimerInfo> _pausedTimers = new List<TimerInfo>();
|
||||
|
||||
TimerInfo _exclusiveTimer;
|
||||
|
||||
static void OnSpawned(
|
||||
TimerInfo exclusiveTimer, TimedBlock instance)
|
||||
{
|
||||
Assert.That(instance._pausedTimers.Count == 0);
|
||||
|
||||
instance._exclusiveTimer = exclusiveTimer;
|
||||
|
||||
foreach (var timer in _timers.Values)
|
||||
{
|
||||
if (exclusiveTimer == timer)
|
||||
{
|
||||
Assert.That(!timer.IsRunning);
|
||||
timer.Resume();
|
||||
}
|
||||
else if (timer.IsRunning)
|
||||
{
|
||||
timer.Pause();
|
||||
instance._pausedTimers.Add(timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnDespawned(TimedBlock instance)
|
||||
{
|
||||
Assert.That(instance._exclusiveTimer.IsRunning);
|
||||
instance._exclusiveTimer.Pause();
|
||||
|
||||
foreach (var timer in instance._pausedTimers)
|
||||
{
|
||||
Assert.That(!timer.IsRunning);
|
||||
timer.Resume();
|
||||
}
|
||||
|
||||
instance._pausedTimers.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Pool.Despawn(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class TimerInfo
|
||||
{
|
||||
readonly Stopwatch _timer;
|
||||
|
||||
public TimerInfo()
|
||||
{
|
||||
_timer = new Stopwatch();
|
||||
}
|
||||
|
||||
public int CallCount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public double TotalMilliseconds
|
||||
{
|
||||
get { return _timer.Elapsed.TotalMilliseconds; }
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return _timer.IsRunning; }
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_timer.Reset();
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
_timer.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81cad1ee2d8c20942a68a4228e09ff1d
|
||||
timeCreated: 1537522729
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,272 @@
|
||||
//#define ZEN_DO_NOT_USE_COMPILED_EXPRESSIONS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using ModestTree;
|
||||
#if !NOT_UNITY3D
|
||||
using UnityEngine;
|
||||
#endif
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
public static class ReflectionInfoTypeInfoConverter
|
||||
{
|
||||
public static InjectTypeInfo.InjectMethodInfo ConvertMethod(
|
||||
ReflectionTypeInfo.InjectMethodInfo injectMethod)
|
||||
{
|
||||
var methodInfo = injectMethod.MethodInfo;
|
||||
var action = TryCreateActionForMethod(methodInfo);
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
action = (obj, args) => methodInfo.Invoke(obj, args);
|
||||
}
|
||||
|
||||
return new InjectTypeInfo.InjectMethodInfo(
|
||||
action,
|
||||
injectMethod.Parameters.Select(x => x.InjectableInfo).ToArray(),
|
||||
methodInfo.Name);
|
||||
}
|
||||
|
||||
public static InjectTypeInfo.InjectConstructorInfo ConvertConstructor(
|
||||
ReflectionTypeInfo.InjectConstructorInfo injectConstructor, Type type)
|
||||
{
|
||||
return new InjectTypeInfo.InjectConstructorInfo(
|
||||
TryCreateFactoryMethod(type, injectConstructor),
|
||||
injectConstructor.Parameters.Select(x => x.InjectableInfo).ToArray());
|
||||
}
|
||||
|
||||
public static InjectTypeInfo.InjectMemberInfo ConvertField(
|
||||
Type parentType, ReflectionTypeInfo.InjectFieldInfo injectField)
|
||||
{
|
||||
return new InjectTypeInfo.InjectMemberInfo(
|
||||
GetSetter(parentType, injectField.FieldInfo), injectField.InjectableInfo);
|
||||
}
|
||||
|
||||
public static InjectTypeInfo.InjectMemberInfo ConvertProperty(
|
||||
Type parentType, ReflectionTypeInfo.InjectPropertyInfo injectProperty)
|
||||
{
|
||||
return new InjectTypeInfo.InjectMemberInfo(
|
||||
GetSetter(parentType, injectProperty.PropertyInfo), injectProperty.InjectableInfo);
|
||||
}
|
||||
|
||||
static ZenFactoryMethod TryCreateFactoryMethod(
|
||||
Type type, ReflectionTypeInfo.InjectConstructorInfo reflectionInfo)
|
||||
{
|
||||
#if !NOT_UNITY3D
|
||||
if (type.DerivesFromOrEqual<Component>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (type.IsAbstract())
|
||||
{
|
||||
Assert.That(reflectionInfo.Parameters.IsEmpty());
|
||||
return null;
|
||||
}
|
||||
|
||||
var constructor = reflectionInfo.ConstructorInfo;
|
||||
|
||||
var factoryMethod = TryCreateFactoryMethodCompiledLambdaExpression(type, constructor);
|
||||
|
||||
if (factoryMethod == null)
|
||||
{
|
||||
if (constructor == null)
|
||||
{
|
||||
// No choice in this case except to use the slow Activator.CreateInstance
|
||||
// as far as I know
|
||||
// This should be rare though and only seems to occur when instantiating
|
||||
// structs on platforms that don't support lambda expressions
|
||||
// Non-structs should always have a default constructor
|
||||
factoryMethod = args =>
|
||||
{
|
||||
Assert.That(args.Length == 0);
|
||||
return Activator.CreateInstance(type, new object[0]);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
factoryMethod = constructor.Invoke;
|
||||
}
|
||||
}
|
||||
|
||||
return factoryMethod;
|
||||
}
|
||||
|
||||
static ZenFactoryMethod TryCreateFactoryMethodCompiledLambdaExpression(
|
||||
Type type, ConstructorInfo constructor)
|
||||
{
|
||||
#if NET_4_6 && !ENABLE_IL2CPP && !ZEN_DO_NOT_USE_COMPILED_EXPRESSIONS
|
||||
|
||||
if (type.ContainsGenericParameters)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterExpression param = Expression.Parameter(typeof(object[]));
|
||||
|
||||
if (constructor == null)
|
||||
{
|
||||
return Expression.Lambda<ZenFactoryMethod>(
|
||||
Expression.Convert(
|
||||
Expression.New(type), typeof(object)), param).Compile();
|
||||
}
|
||||
|
||||
ParameterInfo[] par = constructor.GetParameters();
|
||||
Expression[] args = new Expression[par.Length];
|
||||
|
||||
for (int i = 0; i != par.Length; ++i)
|
||||
{
|
||||
args[i] = Expression.Convert(
|
||||
Expression.ArrayIndex(
|
||||
param, Expression.Constant(i)), par[i].ParameterType);
|
||||
}
|
||||
|
||||
return Expression.Lambda<ZenFactoryMethod>(
|
||||
Expression.Convert(
|
||||
Expression.New(constructor, args), typeof(object)), param).Compile();
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ZenInjectMethod TryCreateActionForMethod(MethodInfo methodInfo)
|
||||
{
|
||||
#if NET_4_6 && !ENABLE_IL2CPP && !ZEN_DO_NOT_USE_COMPILED_EXPRESSIONS
|
||||
|
||||
if (methodInfo.DeclaringType.ContainsGenericParameters)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterInfo[] par = methodInfo.GetParameters();
|
||||
|
||||
if (par.Any(x => x.ParameterType.ContainsGenericParameters))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Expression[] args = new Expression[par.Length];
|
||||
ParameterExpression argsParam = Expression.Parameter(typeof(object[]));
|
||||
ParameterExpression instanceParam = Expression.Parameter(typeof(object));
|
||||
|
||||
for (int i = 0; i != par.Length; ++i)
|
||||
{
|
||||
args[i] = Expression.Convert(
|
||||
Expression.ArrayIndex(
|
||||
argsParam, Expression.Constant(i)), par[i].ParameterType);
|
||||
}
|
||||
|
||||
return Expression.Lambda<ZenInjectMethod>(
|
||||
Expression.Call(
|
||||
Expression.Convert(instanceParam, methodInfo.DeclaringType), methodInfo, args),
|
||||
instanceParam, argsParam).Compile();
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !(UNITY_WSA && ENABLE_DOTNET) || UNITY_EDITOR
|
||||
static IEnumerable<FieldInfo> GetAllFields(Type t, BindingFlags flags)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
return Enumerable.Empty<FieldInfo>();
|
||||
}
|
||||
|
||||
return t.GetFields(flags).Concat(GetAllFields(t.BaseType, flags)).Distinct();
|
||||
}
|
||||
|
||||
static ZenMemberSetterMethod GetOnlyPropertySetter(
|
||||
Type parentType,
|
||||
string propertyName)
|
||||
{
|
||||
Assert.That(parentType != null);
|
||||
Assert.That(!string.IsNullOrEmpty(propertyName));
|
||||
|
||||
var allFields = GetAllFields(
|
||||
parentType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).ToList();
|
||||
|
||||
var writeableFields = allFields.Where(f => f.Name == string.Format("<" + propertyName + ">k__BackingField", propertyName)).ToList();
|
||||
|
||||
if (!writeableFields.Any())
|
||||
{
|
||||
throw new ZenjectException(string.Format(
|
||||
"Can't find backing field for get only property {0} on {1}.\r\n{2}",
|
||||
propertyName, parentType.FullName, string.Join(";", allFields.Select(f => f.Name).ToArray())));
|
||||
}
|
||||
|
||||
return (injectable, value) => writeableFields.ForEach(f => f.SetValue(injectable, value));
|
||||
}
|
||||
#endif
|
||||
|
||||
static ZenMemberSetterMethod GetSetter(Type parentType, MemberInfo memInfo)
|
||||
{
|
||||
var setterMethod = TryGetSetterAsCompiledExpression(parentType, memInfo);
|
||||
|
||||
if (setterMethod != null)
|
||||
{
|
||||
return setterMethod;
|
||||
}
|
||||
|
||||
var fieldInfo = memInfo as FieldInfo;
|
||||
var propInfo = memInfo as PropertyInfo;
|
||||
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return ((injectable, value) => fieldInfo.SetValue(injectable, value));
|
||||
}
|
||||
|
||||
Assert.IsNotNull(propInfo);
|
||||
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return ((object injectable, object value) => propInfo.SetValue(injectable, value, null));
|
||||
#else
|
||||
if (propInfo.CanWrite)
|
||||
{
|
||||
return ((injectable, value) => propInfo.SetValue(injectable, value, null));
|
||||
}
|
||||
|
||||
return GetOnlyPropertySetter(parentType, propInfo.Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ZenMemberSetterMethod TryGetSetterAsCompiledExpression(Type parentType, MemberInfo memInfo)
|
||||
{
|
||||
#if NET_4_6 && !ENABLE_IL2CPP && !ZEN_DO_NOT_USE_COMPILED_EXPRESSIONS
|
||||
|
||||
if (parentType.ContainsGenericParameters)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fieldInfo = memInfo as FieldInfo;
|
||||
var propInfo = memInfo as PropertyInfo;
|
||||
|
||||
// It seems that for readonly fields, we have to use the slower approach below
|
||||
// As discussed here: https://www.productiverage.com/trying-to-set-a-readonly-autoproperty-value-externally-plus-a-little-benchmarkdotnet
|
||||
// We have to skip value types because those can only be set by reference using an lambda expression
|
||||
if (!parentType.IsValueType() && (fieldInfo == null || !fieldInfo.IsInitOnly) && (propInfo == null || propInfo.CanWrite))
|
||||
{
|
||||
Type memberType = fieldInfo != null
|
||||
? fieldInfo.FieldType : propInfo.PropertyType;
|
||||
|
||||
var typeParam = Expression.Parameter(typeof(object));
|
||||
var valueParam = Expression.Parameter(typeof(object));
|
||||
|
||||
return Expression.Lambda<ZenMemberSetterMethod>(
|
||||
Expression.Assign(
|
||||
Expression.MakeMemberAccess(Expression.Convert(typeParam, parentType), memInfo),
|
||||
Expression.Convert(valueParam, memberType)),
|
||||
typeParam, valueParam).Compile();
|
||||
}
|
||||
#endif
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e36c348f078bc444fa1e5b22aca27bad
|
||||
timeCreated: 1536916212
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
[NoReflectionBaking]
|
||||
public class ReflectionTypeInfo
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly Type BaseType;
|
||||
public readonly List<InjectPropertyInfo> InjectProperties;
|
||||
public readonly List<InjectFieldInfo> InjectFields;
|
||||
public readonly InjectConstructorInfo InjectConstructor;
|
||||
public readonly List<InjectMethodInfo> InjectMethods;
|
||||
|
||||
public ReflectionTypeInfo(
|
||||
Type type,
|
||||
Type baseType,
|
||||
InjectConstructorInfo injectConstructor,
|
||||
List<InjectMethodInfo> injectMethods,
|
||||
List<InjectFieldInfo> injectFields,
|
||||
List<InjectPropertyInfo> injectProperties)
|
||||
{
|
||||
Type = type;
|
||||
BaseType = baseType;
|
||||
InjectFields = injectFields;
|
||||
InjectConstructor = injectConstructor;
|
||||
InjectMethods = injectMethods;
|
||||
InjectProperties = injectProperties;
|
||||
}
|
||||
|
||||
[NoReflectionBaking]
|
||||
public class InjectFieldInfo
|
||||
{
|
||||
public readonly FieldInfo FieldInfo;
|
||||
public readonly InjectableInfo InjectableInfo;
|
||||
|
||||
public InjectFieldInfo(
|
||||
FieldInfo fieldInfo,
|
||||
InjectableInfo injectableInfo)
|
||||
{
|
||||
InjectableInfo = injectableInfo;
|
||||
FieldInfo = fieldInfo;
|
||||
}
|
||||
}
|
||||
|
||||
[NoReflectionBaking]
|
||||
public class InjectParameterInfo
|
||||
{
|
||||
public readonly ParameterInfo ParameterInfo;
|
||||
public readonly InjectableInfo InjectableInfo;
|
||||
|
||||
public InjectParameterInfo(
|
||||
ParameterInfo parameterInfo,
|
||||
InjectableInfo injectableInfo)
|
||||
{
|
||||
InjectableInfo = injectableInfo;
|
||||
ParameterInfo = parameterInfo;
|
||||
}
|
||||
}
|
||||
|
||||
[NoReflectionBaking]
|
||||
public class InjectPropertyInfo
|
||||
{
|
||||
public readonly PropertyInfo PropertyInfo;
|
||||
public readonly InjectableInfo InjectableInfo;
|
||||
|
||||
public InjectPropertyInfo(
|
||||
PropertyInfo propertyInfo,
|
||||
InjectableInfo injectableInfo)
|
||||
{
|
||||
InjectableInfo = injectableInfo;
|
||||
PropertyInfo = propertyInfo;
|
||||
}
|
||||
}
|
||||
|
||||
[NoReflectionBaking]
|
||||
public class InjectMethodInfo
|
||||
{
|
||||
public readonly MethodInfo MethodInfo;
|
||||
public readonly List<InjectParameterInfo> Parameters;
|
||||
|
||||
public InjectMethodInfo(
|
||||
MethodInfo methodInfo,
|
||||
List<InjectParameterInfo> parameters)
|
||||
{
|
||||
MethodInfo = methodInfo;
|
||||
Parameters = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
[NoReflectionBaking]
|
||||
public class InjectConstructorInfo
|
||||
{
|
||||
public readonly ConstructorInfo ConstructorInfo;
|
||||
public readonly List<InjectParameterInfo> Parameters;
|
||||
|
||||
public InjectConstructorInfo(
|
||||
ConstructorInfo constructorInfo,
|
||||
List<InjectParameterInfo> parameters)
|
||||
{
|
||||
ConstructorInfo = constructorInfo;
|
||||
Parameters = parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5490313f008f146458b6228165838735
|
||||
timeCreated: 1536916212
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ModestTree
|
||||
{
|
||||
public static class ReflectionUtil
|
||||
{
|
||||
public static Array CreateArray(Type elementType, List<object> instances)
|
||||
{
|
||||
var array = Array.CreateInstance(elementType, instances.Count);
|
||||
|
||||
for (int i = 0; i < instances.Count; i++)
|
||||
{
|
||||
var instance = instances[i];
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
Assert.That(instance.GetType().DerivesFromOrEqual(elementType),
|
||||
"Wrong type when creating array, expected something assignable from '"+ elementType +"', but found '" + instance.GetType() + "'");
|
||||
}
|
||||
|
||||
array.SetValue(instance, i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static IList CreateGenericList(Type elementType, List<object> instances)
|
||||
{
|
||||
var genericType = typeof(List<>).MakeGenericType(elementType);
|
||||
|
||||
var list = (IList)Activator.CreateInstance(genericType);
|
||||
|
||||
for (int i = 0; i < instances.Count; i++)
|
||||
{
|
||||
var instance = instances[i];
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
Assert.That(instance.GetType().DerivesFromOrEqual(elementType),
|
||||
"Wrong type when creating generic list, expected something assignable from '"+ elementType +"', but found '" + instance.GetType() + "'");
|
||||
}
|
||||
|
||||
list.Add(instance);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static string ToDebugString(this MethodInfo method)
|
||||
{
|
||||
return "{0}.{1}".Fmt(method.DeclaringType.PrettyName(), method.Name);
|
||||
}
|
||||
|
||||
public static string ToDebugString(this Action action)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1>(this Action<TParam1> action)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2>(this Action<TParam1, TParam2> action)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3>(this Action<TParam1, TParam2, TParam3> action)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3, TParam4>(this Action<TParam1, TParam2, TParam3, TParam4> action)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3, TParam4, TParam5>(this
|
||||
#if NET_4_6
|
||||
Action<TParam1, TParam2, TParam3, TParam4, TParam5> action)
|
||||
#else
|
||||
ModestTree.Util.Action<TParam1, TParam2, TParam3, TParam4, TParam5> action)
|
||||
#endif
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6>(this
|
||||
#if NET_4_6
|
||||
Action<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6> action)
|
||||
#else
|
||||
ModestTree.Util.Action<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6> action)
|
||||
#endif
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return action.ToString();
|
||||
#else
|
||||
return action.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1>(this Func<TParam1> func)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return func.ToString();
|
||||
#else
|
||||
return func.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2>(this Func<TParam1, TParam2> func)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return func.ToString();
|
||||
#else
|
||||
return func.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3>(this Func<TParam1, TParam2, TParam3> func)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return func.ToString();
|
||||
#else
|
||||
return func.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ToDebugString<TParam1, TParam2, TParam3, TParam4>(this Func<TParam1, TParam2, TParam3, TParam4> func)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return func.ToString();
|
||||
#else
|
||||
return func.Method.ToDebugString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c74b10dac7e87e4095f8e3157eb040d
|
||||
timeCreated: 1520399530
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ModestTree;
|
||||
using Zenject.Internal;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public delegate InjectTypeInfo ZenTypeInfoGetter();
|
||||
|
||||
public enum ReflectionBakingCoverageModes
|
||||
{
|
||||
FallbackToDirectReflection,
|
||||
NoCheckAssumeFullCoverage,
|
||||
FallbackToDirectReflectionWithWarning
|
||||
}
|
||||
|
||||
public static class TypeAnalyzer
|
||||
{
|
||||
static Dictionary<Type, InjectTypeInfo> _typeInfo = new Dictionary<Type, InjectTypeInfo>();
|
||||
|
||||
// We store this separately from InjectTypeInfo because this flag is needed for contract
|
||||
// types whereas InjectTypeInfo is only needed for types that are instantiated, and
|
||||
// we want to minimize the types that generate InjectTypeInfo for
|
||||
static Dictionary<Type, bool> _allowDuringValidation = new Dictionary<Type, bool>();
|
||||
|
||||
// Use double underscores for generated methods since this is also what the C# compiler does
|
||||
// for things like anonymous methods
|
||||
public const string ReflectionBakingGetInjectInfoMethodName = "__zenCreateInjectTypeInfo";
|
||||
public const string ReflectionBakingFactoryMethodName = "__zenCreate";
|
||||
public const string ReflectionBakingInjectMethodPrefix = "__zenInjectMethod";
|
||||
public const string ReflectionBakingFieldSetterPrefix = "__zenFieldSetter";
|
||||
public const string ReflectionBakingPropertySetterPrefix = "__zenPropertySetter";
|
||||
|
||||
public static ReflectionBakingCoverageModes ReflectionBakingCoverageMode
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public static bool ShouldAllowDuringValidation<T>()
|
||||
{
|
||||
return ShouldAllowDuringValidation(typeof(T));
|
||||
}
|
||||
|
||||
public static bool ShouldAllowDuringValidation(Type type)
|
||||
{
|
||||
bool shouldAllow;
|
||||
|
||||
if (!_allowDuringValidation.TryGetValue(type, out shouldAllow))
|
||||
{
|
||||
shouldAllow = ShouldAllowDuringValidationInternal(type);
|
||||
_allowDuringValidation.Add(type, shouldAllow);
|
||||
}
|
||||
|
||||
return shouldAllow;
|
||||
}
|
||||
|
||||
static bool ShouldAllowDuringValidationInternal(Type type)
|
||||
{
|
||||
// During validation, do not instantiate or inject anything except for
|
||||
// Installers, IValidatable's, or types marked with attribute ZenjectAllowDuringValidation
|
||||
// You would typically use ZenjectAllowDuringValidation attribute for data that you
|
||||
// inject into factories
|
||||
|
||||
if (type.DerivesFrom<IInstaller>() || type.DerivesFrom<IValidatable>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !NOT_UNITY3D
|
||||
if (type.DerivesFrom<Context>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
return type.GetTypeInfo().GetCustomAttribute<ZenjectAllowDuringValidationAttribute>() != null;
|
||||
#else
|
||||
return type.HasAttribute<ZenjectAllowDuringValidationAttribute>();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool HasInfo<T>()
|
||||
{
|
||||
return HasInfo(typeof(T));
|
||||
}
|
||||
|
||||
public static bool HasInfo(Type type)
|
||||
{
|
||||
return TryGetInfo(type) != null;
|
||||
}
|
||||
|
||||
public static InjectTypeInfo GetInfo<T>()
|
||||
{
|
||||
return GetInfo(typeof(T));
|
||||
}
|
||||
|
||||
public static InjectTypeInfo GetInfo(Type type)
|
||||
{
|
||||
var info = TryGetInfo(type);
|
||||
Assert.IsNotNull(info, "Unable to get type info for type '{0}'", type);
|
||||
return info;
|
||||
}
|
||||
|
||||
public static InjectTypeInfo TryGetInfo<T>()
|
||||
{
|
||||
return TryGetInfo(typeof(T));
|
||||
}
|
||||
|
||||
public static InjectTypeInfo TryGetInfo(Type type)
|
||||
{
|
||||
InjectTypeInfo info;
|
||||
|
||||
#if ZEN_MULTITHREADING
|
||||
lock (_typeInfo)
|
||||
#endif
|
||||
{
|
||||
if (_typeInfo.TryGetValue(type, out info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using (ProfileBlock.Start("Zenject Reflection"))
|
||||
#endif
|
||||
{
|
||||
info = GetInfoInternal(type);
|
||||
}
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
Assert.IsEqual(info.Type, type);
|
||||
Assert.IsNull(info.BaseTypeInfo);
|
||||
|
||||
var baseType = type.BaseType();
|
||||
|
||||
if (baseType != null && !ShouldSkipTypeAnalysis(baseType))
|
||||
{
|
||||
info.BaseTypeInfo = TryGetInfo(baseType);
|
||||
}
|
||||
}
|
||||
|
||||
#if ZEN_MULTITHREADING
|
||||
lock (_typeInfo)
|
||||
#endif
|
||||
{
|
||||
_typeInfo[type] = info;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static InjectTypeInfo GetInfoInternal(Type type)
|
||||
{
|
||||
if (ShouldSkipTypeAnalysis(type))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
// Make sure that the static constructor logic doesn't inflate our profile measurements
|
||||
using (ProfileTimers.CreateTimedBlock("User Code"))
|
||||
{
|
||||
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Type Analysis - Calling Baked Reflection Getter"))
|
||||
#endif
|
||||
{
|
||||
var getInfoMethod = type.GetMethod(
|
||||
ReflectionBakingGetInjectInfoMethodName,
|
||||
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
|
||||
if (getInfoMethod != null)
|
||||
{
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
var infoGetter = (ZenTypeInfoGetter)getInfoMethod.CreateDelegate(
|
||||
typeof(ZenTypeInfoGetter), null);
|
||||
#else
|
||||
var infoGetter = ((ZenTypeInfoGetter)Delegate.CreateDelegate(
|
||||
typeof(ZenTypeInfoGetter), getInfoMethod));
|
||||
#endif
|
||||
|
||||
return infoGetter();
|
||||
}
|
||||
}
|
||||
|
||||
if (ReflectionBakingCoverageMode == ReflectionBakingCoverageModes.NoCheckAssumeFullCoverage)
|
||||
{
|
||||
// If we are confident that the reflection baking supplies all the injection information,
|
||||
// then we can avoid the costs of doing reflection on types that were not covered
|
||||
// by the baking
|
||||
return null;
|
||||
}
|
||||
|
||||
#if !(UNITY_WSA && ENABLE_DOTNET) || UNITY_EDITOR
|
||||
if (ReflectionBakingCoverageMode == ReflectionBakingCoverageModes.FallbackToDirectReflectionWithWarning)
|
||||
{
|
||||
Log.Warn("No reflection baking information found for type '{0}' - using more costly direct reflection instead", type);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Type Analysis - Direct Reflection"))
|
||||
#endif
|
||||
{
|
||||
return CreateTypeInfoFromReflection(type);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShouldSkipTypeAnalysis(Type type)
|
||||
{
|
||||
return type == null || type.IsEnum() || type.IsArray || type.IsInterface()
|
||||
|| type.ContainsGenericParameters() || IsStaticType(type)
|
||||
|| type == typeof(object);
|
||||
}
|
||||
|
||||
static bool IsStaticType(Type type)
|
||||
{
|
||||
// Apparently this is unique to static classes
|
||||
return type.IsAbstract() && type.IsSealed();
|
||||
}
|
||||
|
||||
static InjectTypeInfo CreateTypeInfoFromReflection(Type type)
|
||||
{
|
||||
var reflectionInfo = ReflectionTypeAnalyzer.GetReflectionInfo(type);
|
||||
|
||||
var injectConstructor = ReflectionInfoTypeInfoConverter.ConvertConstructor(
|
||||
reflectionInfo.InjectConstructor, type);
|
||||
|
||||
var injectMethods = reflectionInfo.InjectMethods.Select(
|
||||
ReflectionInfoTypeInfoConverter.ConvertMethod).ToArray();
|
||||
|
||||
var memberInfos = reflectionInfo.InjectFields.Select(
|
||||
x => ReflectionInfoTypeInfoConverter.ConvertField(type, x)).Concat(
|
||||
reflectionInfo.InjectProperties.Select(
|
||||
x => ReflectionInfoTypeInfoConverter.ConvertProperty(type, x))).ToArray();
|
||||
|
||||
return new InjectTypeInfo(
|
||||
type, injectConstructor, injectMethods, memberInfos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7359cd850f5a96d47ad4606d14dac326
|
||||
timeCreated: 1461708051
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
#if !NOT_UNITY3D
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace ModestTree.Util
|
||||
{
|
||||
public static class UnityUtil
|
||||
{
|
||||
public static IEnumerable<Scene> AllScenes
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
{
|
||||
yield return SceneManager.GetSceneAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Scene> AllLoadedScenes
|
||||
{
|
||||
get { return AllScenes.Where(scene => scene.isLoaded); }
|
||||
}
|
||||
|
||||
public static bool IsAltKeyDown
|
||||
{
|
||||
get { return Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt); }
|
||||
}
|
||||
|
||||
public static bool IsControlKeyDown
|
||||
{
|
||||
get { return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); }
|
||||
}
|
||||
|
||||
public static bool IsShiftKeyDown
|
||||
{
|
||||
get { return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); }
|
||||
}
|
||||
|
||||
public static bool WasShiftKeyJustPressed
|
||||
{
|
||||
get { return Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKeyDown(KeyCode.RightShift); }
|
||||
}
|
||||
|
||||
public static bool WasAltKeyJustPressed
|
||||
{
|
||||
get { return Input.GetKeyDown(KeyCode.LeftAlt) || Input.GetKeyDown(KeyCode.RightAlt); }
|
||||
}
|
||||
|
||||
public static int GetDepthLevel(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1 + GetDepthLevel(transform.parent);
|
||||
}
|
||||
|
||||
public static GameObject GetRootParentOrSelf(GameObject gameObject)
|
||||
{
|
||||
return GetParentsAndSelf(gameObject.transform).Select(x => x.gameObject).LastOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> GetParents(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var ancestor in GetParentsAndSelf(transform.parent))
|
||||
{
|
||||
yield return ancestor;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> GetParentsAndSelf(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return transform;
|
||||
|
||||
foreach (var ancestor in GetParentsAndSelf(transform.parent))
|
||||
{
|
||||
yield return ancestor;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Component> GetComponentsInChildrenTopDown(GameObject gameObject, bool includeInactive)
|
||||
{
|
||||
return gameObject.GetComponentsInChildren<Component>(includeInactive)
|
||||
.OrderBy(x =>
|
||||
x == null ? int.MinValue : GetDepthLevel(x.transform));
|
||||
}
|
||||
|
||||
public static IEnumerable<Component> GetComponentsInChildrenBottomUp(GameObject gameObject, bool includeInactive)
|
||||
{
|
||||
return gameObject.GetComponentsInChildren<Component>(includeInactive)
|
||||
.OrderByDescending(x =>
|
||||
x == null ? int.MinValue : GetDepthLevel(x.transform));
|
||||
}
|
||||
|
||||
public static IEnumerable<GameObject> GetDirectChildrenAndSelf(GameObject obj)
|
||||
{
|
||||
yield return obj;
|
||||
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
yield return child.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GameObject> GetDirectChildren(GameObject obj)
|
||||
{
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
yield return child.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GameObject> GetAllGameObjects()
|
||||
{
|
||||
return GameObject.FindObjectsOfType<Transform>().Select(x => x.gameObject);
|
||||
}
|
||||
|
||||
public static List<GameObject> GetAllRootGameObjects()
|
||||
{
|
||||
return GetAllGameObjects().Where(x => x.transform.parent == null).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 136cacfad8fe5404aad05dda29a777e4
|
||||
timeCreated: 1461708048
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ModestTree;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public static class ValidationUtil
|
||||
{
|
||||
// This method can be used during validation for cases where we need to pass arguments
|
||||
public static List<TypeValuePair> CreateDefaultArgs(params Type[] argTypes)
|
||||
{
|
||||
return argTypes.Select(x => new TypeValuePair(x, x.GetDefaultValue())).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 018820e0bcd9a4049a305127c0cf1407
|
||||
timeCreated: 1461708048
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class ZenAutoInjecter : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
ContainerSources _containerSource = ContainerSources.SearchHierarchy;
|
||||
|
||||
bool _hasInjected;
|
||||
|
||||
public ContainerSources ContainerSource
|
||||
{
|
||||
get { return _containerSource; }
|
||||
set { _containerSource = value; }
|
||||
}
|
||||
|
||||
// Make sure they don't cause injection to happen twice
|
||||
[Inject]
|
||||
public void Construct()
|
||||
{
|
||||
if (!_hasInjected)
|
||||
{
|
||||
throw Assert.CreateException(
|
||||
"ZenAutoInjecter was injected! Do not use ZenAutoInjecter for objects that are instantiated through zenject or which exist in the initial scene hierarchy");
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
_hasInjected = true;
|
||||
LookupContainer().InjectGameObject(gameObject);
|
||||
}
|
||||
|
||||
DiContainer LookupContainer()
|
||||
{
|
||||
if (_containerSource == ContainerSources.ProjectContext)
|
||||
{
|
||||
return ProjectContext.Instance.Container;
|
||||
}
|
||||
|
||||
if (_containerSource == ContainerSources.SceneContext)
|
||||
{
|
||||
return GetContainerForCurrentScene();
|
||||
}
|
||||
|
||||
Assert.IsEqual(_containerSource, ContainerSources.SearchHierarchy);
|
||||
|
||||
var parentContext = transform.GetComponentInParent<Context>();
|
||||
|
||||
if (parentContext != null)
|
||||
{
|
||||
return parentContext.Container;
|
||||
}
|
||||
|
||||
return GetContainerForCurrentScene();
|
||||
}
|
||||
|
||||
DiContainer GetContainerForCurrentScene()
|
||||
{
|
||||
return ProjectContext.Instance.Container.Resolve<SceneContextRegistry>()
|
||||
.GetContainerForScene(gameObject.scene);
|
||||
}
|
||||
|
||||
public enum ContainerSources
|
||||
{
|
||||
SceneContext,
|
||||
ProjectContext,
|
||||
SearchHierarchy
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 717a02054ef2699498e6bd4234fd7995
|
||||
timeCreated: 1510660712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
public static class ZenPools
|
||||
{
|
||||
#if ZEN_INTERNAL_NO_POOLS
|
||||
public static InjectContext SpawnInjectContext(DiContainer container, Type memberType)
|
||||
{
|
||||
return new InjectContext(container, memberType);
|
||||
}
|
||||
|
||||
public static void DespawnInjectContext(InjectContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public static List<T> SpawnList<T>()
|
||||
{
|
||||
return new List<T>();
|
||||
}
|
||||
|
||||
public static void DespawnList<T>(List<T> list)
|
||||
{
|
||||
}
|
||||
|
||||
public static void DespawnArray<T>(T[] arr)
|
||||
{
|
||||
}
|
||||
|
||||
public static T[] SpawnArray<T>(int length)
|
||||
{
|
||||
return new T[length];
|
||||
}
|
||||
|
||||
public static HashSet<T> SpawnHashSet<T>()
|
||||
{
|
||||
return new HashSet<T>();
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TValue> SpawnDictionary<TKey, TValue>()
|
||||
{
|
||||
return new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
public static void DespawnDictionary<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
}
|
||||
|
||||
public static void DespawnHashSet<T>(HashSet<T> set)
|
||||
{
|
||||
}
|
||||
|
||||
public static LookupId SpawnLookupId(IProvider provider, BindingId bindingId)
|
||||
{
|
||||
return new LookupId(provider, bindingId);
|
||||
}
|
||||
|
||||
public static void DespawnLookupId(LookupId lookupId)
|
||||
{
|
||||
}
|
||||
|
||||
public static BindInfo SpawnBindInfo()
|
||||
{
|
||||
return new BindInfo();
|
||||
}
|
||||
|
||||
public static void DespawnBindInfo(BindInfo bindInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public static BindStatement SpawnStatement()
|
||||
{
|
||||
return new BindStatement();
|
||||
}
|
||||
|
||||
public static void DespawnStatement(BindStatement statement)
|
||||
{
|
||||
}
|
||||
#else
|
||||
static readonly StaticMemoryPool<InjectContext> _contextPool = new StaticMemoryPool<InjectContext>();
|
||||
static readonly StaticMemoryPool<LookupId> _lookupIdPool = new StaticMemoryPool<LookupId>();
|
||||
static readonly StaticMemoryPool<BindInfo> _bindInfoPool = new StaticMemoryPool<BindInfo>();
|
||||
static readonly StaticMemoryPool<BindStatement> _bindStatementPool = new StaticMemoryPool<BindStatement>();
|
||||
|
||||
public static HashSet<T> SpawnHashSet<T>()
|
||||
{
|
||||
return HashSetPool<T>.Instance.Spawn();
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TValue> SpawnDictionary<TKey, TValue>()
|
||||
{
|
||||
return DictionaryPool<TKey, TValue>.Instance.Spawn();
|
||||
}
|
||||
|
||||
public static BindStatement SpawnStatement()
|
||||
{
|
||||
return _bindStatementPool.Spawn();
|
||||
}
|
||||
|
||||
public static void DespawnStatement(BindStatement statement)
|
||||
{
|
||||
statement.Reset();
|
||||
_bindStatementPool.Despawn(statement);
|
||||
}
|
||||
|
||||
public static BindInfo SpawnBindInfo()
|
||||
{
|
||||
return _bindInfoPool.Spawn();
|
||||
}
|
||||
|
||||
public static void DespawnBindInfo(BindInfo bindInfo)
|
||||
{
|
||||
bindInfo.Reset();
|
||||
_bindInfoPool.Despawn(bindInfo);
|
||||
}
|
||||
|
||||
public static void DespawnDictionary<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
DictionaryPool<TKey, TValue>.Instance.Despawn(dictionary);
|
||||
}
|
||||
|
||||
public static void DespawnHashSet<T>(HashSet<T> set)
|
||||
{
|
||||
HashSetPool<T>.Instance.Despawn(set);
|
||||
}
|
||||
|
||||
public static LookupId SpawnLookupId(IProvider provider, BindingId bindingId)
|
||||
{
|
||||
var lookupId = _lookupIdPool.Spawn();
|
||||
|
||||
lookupId.Provider = provider;
|
||||
lookupId.BindingId = bindingId;
|
||||
|
||||
return lookupId;
|
||||
}
|
||||
|
||||
public static void DespawnLookupId(LookupId lookupId)
|
||||
{
|
||||
lookupId.Reset();
|
||||
_lookupIdPool.Despawn(lookupId);
|
||||
}
|
||||
|
||||
public static List<T> SpawnList<T>()
|
||||
{
|
||||
return ListPool<T>.Instance.Spawn();
|
||||
}
|
||||
|
||||
public static void DespawnList<T>(List<T> list)
|
||||
{
|
||||
ListPool<T>.Instance.Despawn(list);
|
||||
}
|
||||
|
||||
public static void DespawnArray<T>(T[] arr)
|
||||
{
|
||||
ArrayPool<T>.GetPool(arr.Length).Despawn(arr);
|
||||
}
|
||||
|
||||
public static T[] SpawnArray<T>(int length)
|
||||
{
|
||||
return ArrayPool<T>.GetPool(length).Spawn();
|
||||
}
|
||||
|
||||
public static InjectContext SpawnInjectContext(DiContainer container, Type memberType)
|
||||
{
|
||||
var context = _contextPool.Spawn();
|
||||
|
||||
context.Container = container;
|
||||
context.MemberType = memberType;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public static void DespawnInjectContext(InjectContext context)
|
||||
{
|
||||
context.Reset();
|
||||
_contextPool.Despawn(context);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static InjectContext SpawnInjectContext(
|
||||
DiContainer container, InjectableInfo injectableInfo, InjectContext currentContext,
|
||||
object targetInstance, Type targetType, object concreteIdentifier)
|
||||
{
|
||||
var context = SpawnInjectContext(container, injectableInfo.MemberType);
|
||||
|
||||
context.ObjectType = targetType;
|
||||
context.ParentContext = currentContext;
|
||||
context.ObjectInstance = targetInstance;
|
||||
context.Identifier = injectableInfo.Identifier;
|
||||
context.MemberName = injectableInfo.MemberName;
|
||||
context.Optional = injectableInfo.Optional;
|
||||
context.SourceType = injectableInfo.SourceType;
|
||||
context.FallBackValue = injectableInfo.DefaultValue;
|
||||
context.ConcreteIdentifier = concreteIdentifier;
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14ff296505fe79448b0c79ec09977477
|
||||
timeCreated: 1535860932
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ModestTree;
|
||||
#if !NOT_UNITY3D
|
||||
using UnityEngine;
|
||||
#endif
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
public static class ReflectionTypeAnalyzer
|
||||
{
|
||||
static readonly HashSet<Type> _injectAttributeTypes;
|
||||
|
||||
static ReflectionTypeAnalyzer()
|
||||
{
|
||||
_injectAttributeTypes = new HashSet<Type>();
|
||||
_injectAttributeTypes.Add(typeof(InjectAttributeBase));
|
||||
}
|
||||
|
||||
public static void AddCustomInjectAttribute<T>()
|
||||
where T : Attribute
|
||||
{
|
||||
AddCustomInjectAttribute(typeof(T));
|
||||
}
|
||||
|
||||
public static void AddCustomInjectAttribute(Type type)
|
||||
{
|
||||
Assert.That(type.DerivesFrom<Attribute>());
|
||||
_injectAttributeTypes.Add(type);
|
||||
}
|
||||
|
||||
public static ReflectionTypeInfo GetReflectionInfo(Type type)
|
||||
{
|
||||
Assert.That(!type.IsEnum(), "Tried to analyze enum type '{0}'. This is not supported", type);
|
||||
Assert.That(!type.IsArray, "Tried to analyze array type '{0}'. This is not supported", type);
|
||||
|
||||
var baseType = type.BaseType();
|
||||
|
||||
if (baseType == typeof(object))
|
||||
{
|
||||
baseType = null;
|
||||
}
|
||||
|
||||
return new ReflectionTypeInfo(
|
||||
type, baseType, GetConstructorInfo(type), GetMethodInfos(type),
|
||||
GetFieldInfos(type), GetPropertyInfos(type));
|
||||
}
|
||||
|
||||
static List<ReflectionTypeInfo.InjectPropertyInfo> GetPropertyInfos(Type type)
|
||||
{
|
||||
return type.DeclaredInstanceProperties()
|
||||
.Where(x => _injectAttributeTypes.Any(a => x.HasAttribute(a)))
|
||||
.Select(x => new ReflectionTypeInfo.InjectPropertyInfo(
|
||||
x, GetInjectableInfoForMember(type, x))).ToList();
|
||||
}
|
||||
|
||||
static List<ReflectionTypeInfo.InjectFieldInfo> GetFieldInfos(Type type)
|
||||
{
|
||||
return type.DeclaredInstanceFields()
|
||||
.Where(x => _injectAttributeTypes.Any(a => x.HasAttribute(a)))
|
||||
.Select(x => new ReflectionTypeInfo.InjectFieldInfo(
|
||||
x, GetInjectableInfoForMember(type, x)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
static List<ReflectionTypeInfo.InjectMethodInfo> GetMethodInfos(Type type)
|
||||
{
|
||||
var injectMethodInfos = new List<ReflectionTypeInfo.InjectMethodInfo>();
|
||||
|
||||
// Note that unlike with fields and properties we use GetCustomAttributes
|
||||
// This is so that we can ignore inherited attributes, which is necessary
|
||||
// otherwise a base class method marked with [Inject] would cause all overridden
|
||||
// derived methods to be added as well
|
||||
var methodInfos = type.DeclaredInstanceMethods()
|
||||
.Where(x => _injectAttributeTypes.Any(a => x.GetCustomAttributes(a, false).Any())).ToList();
|
||||
|
||||
for (int i = 0; i < methodInfos.Count; i++)
|
||||
{
|
||||
var methodInfo = methodInfos[i];
|
||||
var injectAttr = methodInfo.AllAttributes<InjectAttributeBase>().SingleOrDefault();
|
||||
|
||||
if (injectAttr != null)
|
||||
{
|
||||
Assert.That(!injectAttr.Optional && injectAttr.Id == null && injectAttr.Source == InjectSources.Any,
|
||||
"Parameters of InjectAttribute do not apply to constructors and methodInfos");
|
||||
}
|
||||
|
||||
var injectParamInfos = methodInfo.GetParameters()
|
||||
.Select(x => CreateInjectableInfoForParam(type, x)).ToList();
|
||||
|
||||
injectMethodInfos.Add(
|
||||
new ReflectionTypeInfo.InjectMethodInfo(methodInfo, injectParamInfos));
|
||||
}
|
||||
|
||||
return injectMethodInfos;
|
||||
}
|
||||
|
||||
static ReflectionTypeInfo.InjectConstructorInfo GetConstructorInfo(Type type)
|
||||
{
|
||||
var args = new List<ReflectionTypeInfo.InjectParameterInfo>();
|
||||
|
||||
var constructor = TryGetInjectConstructor(type);
|
||||
|
||||
if (constructor != null)
|
||||
{
|
||||
args.AddRange(constructor.GetParameters().Select(
|
||||
x => CreateInjectableInfoForParam(type, x)));
|
||||
}
|
||||
|
||||
return new ReflectionTypeInfo.InjectConstructorInfo(constructor, args);
|
||||
}
|
||||
|
||||
static ReflectionTypeInfo.InjectParameterInfo CreateInjectableInfoForParam(
|
||||
Type parentType, ParameterInfo paramInfo)
|
||||
{
|
||||
var injectAttributes = paramInfo.AllAttributes<InjectAttributeBase>().ToList();
|
||||
|
||||
Assert.That(injectAttributes.Count <= 1,
|
||||
"Found multiple 'Inject' attributes on type parameter '{0}' of type '{1}'. Parameter should only have one", paramInfo.Name, parentType);
|
||||
|
||||
var injectAttr = injectAttributes.SingleOrDefault();
|
||||
|
||||
object identifier = null;
|
||||
bool isOptional = false;
|
||||
InjectSources sourceType = InjectSources.Any;
|
||||
|
||||
if (injectAttr != null)
|
||||
{
|
||||
identifier = injectAttr.Id;
|
||||
isOptional = injectAttr.Optional;
|
||||
sourceType = injectAttr.Source;
|
||||
}
|
||||
|
||||
bool isOptionalWithADefaultValue = (paramInfo.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault;
|
||||
|
||||
return new ReflectionTypeInfo.InjectParameterInfo(
|
||||
paramInfo,
|
||||
new InjectableInfo(
|
||||
isOptionalWithADefaultValue || isOptional,
|
||||
identifier,
|
||||
paramInfo.Name,
|
||||
paramInfo.ParameterType,
|
||||
isOptionalWithADefaultValue ? paramInfo.DefaultValue : null,
|
||||
sourceType));
|
||||
}
|
||||
|
||||
static InjectableInfo GetInjectableInfoForMember(Type parentType, MemberInfo memInfo)
|
||||
{
|
||||
var injectAttributes = memInfo.AllAttributes<InjectAttributeBase>().ToList();
|
||||
|
||||
Assert.That(injectAttributes.Count <= 1,
|
||||
"Found multiple 'Inject' attributes on type field '{0}' of type '{1}'. Field should only container one Inject attribute", memInfo.Name, parentType);
|
||||
|
||||
var injectAttr = injectAttributes.SingleOrDefault();
|
||||
|
||||
object identifier = null;
|
||||
bool isOptional = false;
|
||||
InjectSources sourceType = InjectSources.Any;
|
||||
|
||||
if (injectAttr != null)
|
||||
{
|
||||
identifier = injectAttr.Id;
|
||||
isOptional = injectAttr.Optional;
|
||||
sourceType = injectAttr.Source;
|
||||
}
|
||||
|
||||
Type memberType = memInfo is FieldInfo
|
||||
? ((FieldInfo)memInfo).FieldType : ((PropertyInfo)memInfo).PropertyType;
|
||||
|
||||
return new InjectableInfo(
|
||||
isOptional,
|
||||
identifier,
|
||||
memInfo.Name,
|
||||
memberType,
|
||||
null,
|
||||
sourceType);
|
||||
}
|
||||
|
||||
static ConstructorInfo TryGetInjectConstructor(Type type)
|
||||
{
|
||||
#if !NOT_UNITY3D
|
||||
if (type.DerivesFromOrEqual<Component>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (type.IsAbstract())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var constructors = type.Constructors();
|
||||
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
// WP8 generates a dummy constructor with signature (internal Classname(UIntPtr dummy))
|
||||
// So just ignore that
|
||||
constructors = constructors.Where(c => !IsWp8GeneratedConstructor(c)).ToArray();
|
||||
#endif
|
||||
|
||||
if (constructors.IsEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (constructors.HasMoreThan(1))
|
||||
{
|
||||
var explicitConstructor = (from c in constructors where _injectAttributeTypes.Any(a => c.HasAttribute(a)) select c).SingleOrDefault();
|
||||
|
||||
if (explicitConstructor != null)
|
||||
{
|
||||
return explicitConstructor;
|
||||
}
|
||||
|
||||
// If there is only one public constructor then use that
|
||||
// This makes decent sense but is also necessary on WSA sometimes since the WSA generated
|
||||
// constructor can sometimes be private with zero parameters
|
||||
var singlePublicConstructor = constructors.Where(x => x.IsPublic).OnlyOrDefault();
|
||||
|
||||
if (singlePublicConstructor != null)
|
||||
{
|
||||
return singlePublicConstructor;
|
||||
}
|
||||
|
||||
// Choose the one with the least amount of arguments
|
||||
// This might result in some non obvious errors like null reference exceptions
|
||||
// but is probably the best trade-off since it allows zenject to be more compatible
|
||||
// with libraries that don't depend on zenject at all
|
||||
// Discussion here - https://github.com/svermeulen/Zenject/issues/416
|
||||
return constructors.OrderBy(x => x.GetParameters().Count()).First();
|
||||
}
|
||||
|
||||
return constructors[0];
|
||||
}
|
||||
|
||||
#if UNITY_WSA && ENABLE_DOTNET && !UNITY_EDITOR
|
||||
static bool IsWp8GeneratedConstructor(ConstructorInfo c)
|
||||
{
|
||||
ParameterInfo[] args = c.GetParameters();
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return args[0].ParameterType == typeof(UIntPtr)
|
||||
&& (string.IsNullOrEmpty(args[0].Name) || args[0].Name == "dummy");
|
||||
}
|
||||
|
||||
if (args.Length == 2)
|
||||
{
|
||||
return args[0].ParameterType == typeof(UIntPtr)
|
||||
&& args[1].ParameterType == typeof(Int64*)
|
||||
&& (string.IsNullOrEmpty(args[0].Name) || args[0].Name == "dummy")
|
||||
&& (string.IsNullOrEmpty(args[1].Name) || args[1].Name == "dummy");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 046b222c5a2e0994cb5c424ed912f808
|
||||
timeCreated: 1536916211
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ModestTree;
|
||||
using ModestTree.Util;
|
||||
#if !NOT_UNITY3D
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine;
|
||||
#endif
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
public static class ZenUtilInternal
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static GameObject _disabledIndestructibleGameObject;
|
||||
#endif
|
||||
|
||||
// Due to the way that Unity overrides the Equals operator,
|
||||
// normal null checks such as (x == null) do not always work as
|
||||
// expected
|
||||
// In those cases you can use this function which will also
|
||||
// work with non-unity objects
|
||||
public static bool IsNull(System.Object obj)
|
||||
{
|
||||
return obj == null || obj.Equals(null);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// This can be useful if you are running code outside unity
|
||||
// since in that case you have to make sure to avoid calling anything
|
||||
// inside Unity DLLs
|
||||
public static bool IsOutsideUnity()
|
||||
{
|
||||
return AppDomain.CurrentDomain.FriendlyName != "Unity Child Domain";
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool AreFunctionsEqual(Delegate left, Delegate right)
|
||||
{
|
||||
return left.Target == right.Target && left.Method() == right.Method();
|
||||
}
|
||||
|
||||
// Taken from here:
|
||||
// http://stackoverflow.com/questions/28937324/in-c-how-could-i-get-a-classs-inheritance-distance-to-base-class/28937542#28937542
|
||||
public static int GetInheritanceDelta(Type derived, Type parent)
|
||||
{
|
||||
Assert.That(derived.DerivesFromOrEqual(parent));
|
||||
|
||||
if (parent.IsInterface())
|
||||
{
|
||||
// Not sure if we can calculate this so just return 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (derived == parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int distance = 1;
|
||||
|
||||
Type child = derived;
|
||||
|
||||
while ((child = child.BaseType()) != parent)
|
||||
{
|
||||
distance++;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
#if !NOT_UNITY3D
|
||||
public static IEnumerable<SceneContext> GetAllSceneContexts()
|
||||
{
|
||||
foreach (var scene in UnityUtil.AllLoadedScenes)
|
||||
{
|
||||
var contexts = scene.GetRootGameObjects()
|
||||
.SelectMany(root => root.GetComponentsInChildren<SceneContext>()).ToList();
|
||||
|
||||
if (contexts.IsEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.That(contexts.Count == 1,
|
||||
"Found multiple scene contexts in scene '{0}'", scene.name);
|
||||
|
||||
yield return contexts[0];
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddStateMachineBehaviourAutoInjectersInScene(Scene scene)
|
||||
{
|
||||
foreach (var rootObj in GetRootGameObjects(scene))
|
||||
{
|
||||
if (rootObj != null)
|
||||
{
|
||||
AddStateMachineBehaviourAutoInjectersUnderGameObject(rootObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call this before calling GetInjectableMonoBehavioursUnderGameObject to ensure that the StateMachineBehaviour's
|
||||
// also get injected properly
|
||||
// The StateMachineBehaviour's cannot be retrieved until after the Start() method so we
|
||||
// need to use ZenjectStateMachineBehaviourAutoInjecter to do the injection at that
|
||||
// time for us
|
||||
public static void AddStateMachineBehaviourAutoInjectersUnderGameObject(GameObject root)
|
||||
{
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
|
||||
#endif
|
||||
{
|
||||
var animators = root.GetComponentsInChildren<Animator>(true);
|
||||
|
||||
foreach (var animator in animators)
|
||||
{
|
||||
if (animator.gameObject.GetComponent<ZenjectStateMachineBehaviourAutoInjecter>() == null)
|
||||
{
|
||||
animator.gameObject.AddComponent<ZenjectStateMachineBehaviourAutoInjecter>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetInjectableMonoBehavioursInScene(
|
||||
Scene scene, List<MonoBehaviour> monoBehaviours)
|
||||
{
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
|
||||
#endif
|
||||
{
|
||||
foreach (var rootObj in GetRootGameObjects(scene))
|
||||
{
|
||||
if (rootObj != null)
|
||||
{
|
||||
GetInjectableMonoBehavioursUnderGameObjectInternal(rootObj, monoBehaviours);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This method will not return components that are within a GameObjectContext
|
||||
// It returns monobehaviours in a bottom-up order
|
||||
public static void GetInjectableMonoBehavioursUnderGameObject(
|
||||
GameObject gameObject, List<MonoBehaviour> injectableComponents)
|
||||
{
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
|
||||
#endif
|
||||
{
|
||||
GetInjectableMonoBehavioursUnderGameObjectInternal(gameObject, injectableComponents);
|
||||
}
|
||||
}
|
||||
|
||||
static void GetInjectableMonoBehavioursUnderGameObjectInternal(
|
||||
GameObject gameObject, List<MonoBehaviour> injectableComponents)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var monoBehaviours = gameObject.GetComponents<MonoBehaviour>();
|
||||
|
||||
for (int i = 0; i < monoBehaviours.Length; i++)
|
||||
{
|
||||
var monoBehaviour = monoBehaviours[i];
|
||||
|
||||
// Can be null for broken component references
|
||||
if (monoBehaviour != null
|
||||
&& monoBehaviour.GetType().DerivesFromOrEqual<GameObjectContext>())
|
||||
{
|
||||
// Need to make sure we don't inject on any MonoBehaviour's that are below a GameObjectContext
|
||||
// Since that is the responsibility of the GameObjectContext
|
||||
// BUT we do want to inject on the GameObjectContext itself
|
||||
injectableComponents.Add(monoBehaviour);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse first so it adds components bottom up though it shouldn't really matter much
|
||||
// because it should always inject in the dependency order
|
||||
for (int i = 0; i < gameObject.transform.childCount; i++)
|
||||
{
|
||||
var child = gameObject.transform.GetChild(i);
|
||||
|
||||
if (child != null)
|
||||
{
|
||||
GetInjectableMonoBehavioursUnderGameObjectInternal(child.gameObject, injectableComponents);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < monoBehaviours.Length; i++)
|
||||
{
|
||||
var monoBehaviour = monoBehaviours[i];
|
||||
|
||||
// Can be null for broken component references
|
||||
if (monoBehaviour != null
|
||||
&& IsInjectableMonoBehaviourType(monoBehaviour.GetType()))
|
||||
{
|
||||
injectableComponents.Add(monoBehaviour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsInjectableMonoBehaviourType(Type type)
|
||||
{
|
||||
// Do not inject on installers since these are always injected before they are installed
|
||||
return type != null && !type.DerivesFrom<MonoInstaller>() && TypeAnalyzer.HasInfo(type);
|
||||
}
|
||||
|
||||
public static IEnumerable<GameObject> GetRootGameObjects(Scene scene)
|
||||
{
|
||||
#if ZEN_INTERNAL_PROFILING
|
||||
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
|
||||
#endif
|
||||
{
|
||||
if (scene.isLoaded)
|
||||
{
|
||||
return scene.GetRootGameObjects()
|
||||
.Where(x => x.GetComponent<ProjectContext>() == null);
|
||||
}
|
||||
|
||||
// Note: We can't use scene.GetRootObjects() here because that apparently fails with an exception
|
||||
// about the scene not being loaded yet when executed in Awake
|
||||
// We also can't use GameObject.FindObjectsOfType<Transform>() because that does not include inactive game objects
|
||||
// So we use Resources.FindObjectsOfTypeAll, even though that may include prefabs. However, our assumption here
|
||||
// is that prefabs do not have their "scene" property set correctly so this should work
|
||||
//
|
||||
// It's important here that we only inject into root objects that are part of our scene, to properly support
|
||||
// multi-scene editing features of Unity 5.x
|
||||
//
|
||||
// Also, even with older Unity versions, if there is an object that is marked with DontDestroyOnLoad, then it will
|
||||
// be injected multiple times when another scene is loaded
|
||||
//
|
||||
// We also make sure not to inject into the project root objects which are injected by ProjectContext.
|
||||
return Resources.FindObjectsOfTypeAll<GameObject>()
|
||||
.Where(x => x.transform.parent == null
|
||||
&& x.GetComponent<ProjectContext>() == null
|
||||
&& x.scene == scene);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Returns a Transform in the DontDestroyOnLoad scene (or, if we're not in play mode, within the current active scene)
|
||||
// whose GameObject is inactive, and whose hide flags are set to HideAndDontSave. We can instantiate prefabs in here
|
||||
// without any of their Awake() methods firing.
|
||||
public static Transform GetOrCreateInactivePrefabParent()
|
||||
{
|
||||
if(_disabledIndestructibleGameObject == null || (!Application.isPlaying && _disabledIndestructibleGameObject.scene != SceneManager.GetActiveScene()))
|
||||
{
|
||||
var go = new GameObject("ZenUtilInternal_PrefabParent");
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
go.SetActive(false);
|
||||
|
||||
if(Application.isPlaying)
|
||||
{
|
||||
UnityEngine.Object.DontDestroyOnLoad(go);
|
||||
}
|
||||
|
||||
_disabledIndestructibleGameObject = go;
|
||||
}
|
||||
|
||||
return _disabledIndestructibleGameObject.transform;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 401238e59d733ab48aaea1a582241b29
|
||||
timeCreated: 1461708049
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
[NoReflectionBaking]
|
||||
public class ZenjectException : Exception
|
||||
{
|
||||
public ZenjectException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ZenjectException(
|
||||
string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60bbf1e064ae9674185f3f301957c914
|
||||
timeCreated: 1461708050
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,151 @@
|
||||
#if !NOT_UNITY3D
|
||||
|
||||
using System;
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public enum LoadSceneRelationship
|
||||
{
|
||||
// This will use the ProjectContext container as parent for the new scene
|
||||
// This is similar to just running the new scene normally
|
||||
None,
|
||||
// This will use current scene as parent for the new scene
|
||||
// This will allow the new scene to refer to dependencies in the current scene
|
||||
Child,
|
||||
// This will use the parent of the current scene as the parent for the next scene
|
||||
// In most cases this will be the same as None
|
||||
Sibling
|
||||
}
|
||||
|
||||
public class ZenjectSceneLoader
|
||||
{
|
||||
readonly ProjectKernel _projectKernel;
|
||||
readonly DiContainer _sceneContainer;
|
||||
|
||||
public ZenjectSceneLoader(
|
||||
[InjectOptional]
|
||||
SceneContext sceneRoot,
|
||||
ProjectKernel projectKernel)
|
||||
{
|
||||
_projectKernel = projectKernel;
|
||||
_sceneContainer = sceneRoot == null ? null : sceneRoot.Container;
|
||||
}
|
||||
|
||||
public void LoadScene(
|
||||
string sceneName,
|
||||
LoadSceneMode loadMode = LoadSceneMode.Single,
|
||||
Action<DiContainer> extraBindings = null,
|
||||
LoadSceneRelationship containerMode = LoadSceneRelationship.None,
|
||||
Action<DiContainer> extraBindingsLate = null)
|
||||
{
|
||||
PrepareForLoadScene(loadMode, extraBindings, extraBindingsLate, containerMode);
|
||||
|
||||
Assert.That(Application.CanStreamedLevelBeLoaded(sceneName),
|
||||
"Unable to load scene '{0}'", sceneName);
|
||||
|
||||
SceneManager.LoadScene(sceneName, loadMode);
|
||||
|
||||
// It would be nice here to actually verify that the new scene has a SceneContext
|
||||
// if we have extra binding hooks, or LoadSceneRelationship != None, but
|
||||
// we can't do that in this case since the scene isn't loaded until the next frame
|
||||
}
|
||||
|
||||
public AsyncOperation LoadSceneAsync(
|
||||
string sceneName,
|
||||
LoadSceneMode loadMode = LoadSceneMode.Single,
|
||||
Action<DiContainer> extraBindings = null,
|
||||
LoadSceneRelationship containerMode = LoadSceneRelationship.None,
|
||||
Action<DiContainer> extraBindingsLate = null)
|
||||
{
|
||||
PrepareForLoadScene(loadMode, extraBindings, extraBindingsLate, containerMode);
|
||||
|
||||
Assert.That(Application.CanStreamedLevelBeLoaded(sceneName),
|
||||
"Unable to load scene '{0}'", sceneName);
|
||||
|
||||
return SceneManager.LoadSceneAsync(sceneName, loadMode);
|
||||
}
|
||||
|
||||
void PrepareForLoadScene(
|
||||
LoadSceneMode loadMode,
|
||||
Action<DiContainer> extraBindings,
|
||||
Action<DiContainer> extraBindingsLate,
|
||||
LoadSceneRelationship containerMode)
|
||||
{
|
||||
if (loadMode == LoadSceneMode.Single)
|
||||
{
|
||||
Assert.IsEqual(containerMode, LoadSceneRelationship.None);
|
||||
|
||||
// Here we explicitly unload all existing scenes rather than relying on Unity to
|
||||
// do this for us. The reason we do this is to ensure a deterministic destruction
|
||||
// order for everything in the scene and in the container.
|
||||
// See comment at ProjectKernel.OnApplicationQuit for more details
|
||||
_projectKernel.ForceUnloadAllScenes();
|
||||
}
|
||||
|
||||
if (containerMode == LoadSceneRelationship.None)
|
||||
{
|
||||
SceneContext.ParentContainers = null;
|
||||
}
|
||||
else if (containerMode == LoadSceneRelationship.Child)
|
||||
{
|
||||
if (_sceneContainer == null)
|
||||
{
|
||||
SceneContext.ParentContainers = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneContext.ParentContainers = new[] { _sceneContainer };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNotNull(_sceneContainer,
|
||||
"Cannot use LoadSceneRelationship.Sibling when loading scenes from ProjectContext");
|
||||
Assert.IsEqual(containerMode, LoadSceneRelationship.Sibling);
|
||||
SceneContext.ParentContainers = _sceneContainer.ParentContainers;
|
||||
}
|
||||
|
||||
SceneContext.ExtraBindingsInstallMethod = extraBindings;
|
||||
SceneContext.ExtraBindingsLateInstallMethod = extraBindingsLate;
|
||||
}
|
||||
|
||||
public void LoadScene(
|
||||
int sceneIndex,
|
||||
LoadSceneMode loadMode = LoadSceneMode.Single,
|
||||
Action<DiContainer> extraBindings = null,
|
||||
LoadSceneRelationship containerMode = LoadSceneRelationship.None,
|
||||
Action<DiContainer> extraBindingsLate = null)
|
||||
{
|
||||
PrepareForLoadScene(loadMode, extraBindings, extraBindingsLate, containerMode);
|
||||
|
||||
Assert.That(Application.CanStreamedLevelBeLoaded(sceneIndex),
|
||||
"Unable to load scene '{0}'", sceneIndex);
|
||||
|
||||
SceneManager.LoadScene(sceneIndex, loadMode);
|
||||
|
||||
// It would be nice here to actually verify that the new scene has a SceneContext
|
||||
// if we have extra binding hooks, or LoadSceneRelationship != None, but
|
||||
// we can't do that in this case since the scene isn't loaded until the next frame
|
||||
}
|
||||
|
||||
public AsyncOperation LoadSceneAsync(
|
||||
int sceneIndex,
|
||||
LoadSceneMode loadMode = LoadSceneMode.Single,
|
||||
Action<DiContainer> extraBindings = null,
|
||||
LoadSceneRelationship containerMode = LoadSceneRelationship.None,
|
||||
Action<DiContainer> extraBindingsLate = null)
|
||||
{
|
||||
PrepareForLoadScene(loadMode, extraBindings, extraBindingsLate, containerMode);
|
||||
|
||||
Assert.That(Application.CanStreamedLevelBeLoaded(sceneIndex),
|
||||
"Unable to load scene '{0}'", sceneIndex);
|
||||
|
||||
return SceneManager.LoadSceneAsync(sceneIndex, loadMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 097ddf2608210fe44a9d215a1721d857
|
||||
timeCreated: 1461708048
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class ZenjectStateMachineBehaviourAutoInjecter : MonoBehaviour
|
||||
{
|
||||
DiContainer _container;
|
||||
Animator _animator;
|
||||
|
||||
[Inject]
|
||||
public void Construct(DiContainer container)
|
||||
{
|
||||
_container = container;
|
||||
_animator = GetComponent<Animator>();
|
||||
Assert.IsNotNull(_animator);
|
||||
}
|
||||
|
||||
// The unity docs (https://unity3d.com/learn/tutorials/modules/beginner/5-pre-order-beta/state-machine-behaviours)
|
||||
// mention that StateMachineBehaviour's should only be retrieved in the Start method
|
||||
// which is why we do it here
|
||||
public void Start()
|
||||
{
|
||||
// Animator can be null when users create GameObjects directly so in that case
|
||||
// Just don't bother attempting to inject the behaviour classes
|
||||
if (_animator != null)
|
||||
{
|
||||
var behaviours = _animator.GetBehaviours<StateMachineBehaviour>();
|
||||
|
||||
if (behaviours != null)
|
||||
{
|
||||
foreach (var behaviour in behaviours)
|
||||
{
|
||||
_container.Inject(behaviour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 486c69818dffcd14f96ce64502516bbb
|
||||
timeCreated: 1527941118
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -9991
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user