/FishingPrototype/Assets/ThirdParty/Zenject/Source/Main/DiContainer.cs
C# | 1365 lines | 1038 code | 232 blank | 95 comment | 98 complexity | d86f451327f28cf04a8e879f11da772e MD5 | raw file
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using ModestTree;
- using ModestTree.Util;
- using Zenject.Internal;
- #if !NOT_UNITY3D
- using UnityEngine;
- #endif
- namespace Zenject
- {
- public delegate bool BindingCondition(InjectContext c);
- // Responsibilities:
- // - Expose methods to configure object graph via Bind() methods
- // - Build object graphs via Resolve() method
- public class DiContainer : IInstantiator, IResolver, IBinder
- {
- public const string DependencyRootIdentifier = "DependencyRoot";
- readonly Dictionary<BindingId, List<ProviderInfo>> _providers = new Dictionary<BindingId, List<ProviderInfo>>();
- readonly DiContainer _parentContainer;
- readonly Stack<LookupId> _resolvesInProgress = new Stack<LookupId>();
- readonly SingletonProviderCreator _singletonProviderCreator;
- readonly SingletonMarkRegistry _singletonMarkRegistry;
- readonly Queue<IBindingFinalizer> _currentBindings = new Queue<IBindingFinalizer>();
- readonly List<IBindingFinalizer> _processedBindings = new List<IBindingFinalizer>();
- bool _isValidating;
- bool _isInstalling;
- bool _hasDisplayedInstallWarning;
- public DiContainer(bool isValidating)
- {
- _isValidating = isValidating;
- _singletonMarkRegistry = new SingletonMarkRegistry();
- _singletonProviderCreator = new SingletonProviderCreator(this, _singletonMarkRegistry);
- // We can't simply call Bind<DiContainer>().FromInstance(this) here because
- // we don't want these bindings to be included in the Clone() below
- // So just directly add to the provider map instead
- var thisProvider = new InstanceProvider(typeof(DiContainer), this);
- var thisContracts = new Type[]
- {
- typeof(DiContainer), typeof(IBinder), typeof(IResolver), typeof(IInstantiator)
- };
- foreach (var contractType in thisContracts)
- {
- var infoList = new List<ProviderInfo>()
- {
- new ProviderInfo(thisProvider, null)
- };
- var bindingId = new BindingId(contractType, null);
- _providers.Add(bindingId, infoList);
- }
- }
- public DiContainer()
- : this(false)
- {
- }
- public DiContainer(DiContainer parentContainer, bool isValidating)
- : this(isValidating)
- {
- _parentContainer = parentContainer;
- if (parentContainer != null)
- {
- parentContainer.FlushBindings();
- #if !NOT_UNITY3D
- DefaultParent = parentContainer.DefaultParent;
- #endif
- foreach (var binding in parentContainer._processedBindings
- .Where(x => x.InheritInSubContainers))
- {
- _currentBindings.Enqueue(binding);
- }
- FlushBindings();
- }
- }
- public DiContainer(DiContainer parentContainer)
- : this(parentContainer, false)
- {
- }
- // When true, this will throw exceptions whenever we create new game objects
- // This is helpful when used in places like EditorWindowKernel where we can't
- // assume that there is a "scene" to place objects
- public bool AssertOnNewGameObjects
- {
- get;
- set;
- }
- public SingletonMarkRegistry SingletonMarkRegistry
- {
- get
- {
- return _singletonMarkRegistry;
- }
- }
- public SingletonProviderCreator SingletonProviderCreator
- {
- get
- {
- return _singletonProviderCreator;
- }
- }
- #if !NOT_UNITY3D
- public Transform DefaultParent
- {
- get;
- set;
- }
- #endif
- public DiContainer ParentContainer
- {
- get
- {
- return _parentContainer;
- }
- }
- public bool ChecksForCircularDependencies
- {
- get
- {
- #if ZEN_MULTITHREADING
- // When multithreading is supported we can't use a static field to track the lookup
- // TODO: We could look at the inject context though
- return false;
- #else
- return true;
- #endif
- }
- }
- // See comment in IBinder.cs for description
- public bool IsValidating
- {
- get
- {
- return _isValidating;
- }
- }
- // When this is true, it will log warnings when Resolve or Instantiate
- // methods are called
- // Used to ensure that Resolve and Instantiate methods are not called
- // during bind phase. This is important since Resolve and Instantiate
- // make use of the bindings, so if the bindings are not complete then
- // unexpected behaviour can occur
- public bool IsInstalling
- {
- get
- {
- return _isInstalling;
- }
- set
- {
- _isInstalling = value;
- }
- }
- public IEnumerable<BindingId> AllContracts
- {
- get
- {
- FlushBindings();
- return _providers.Keys;
- }
- }
- // DO not run this within Unity!
- // This is only really useful if you are not using any of the Unity bind methods such as
- // FromGameObject, FromPrefab, etc.
- // If you are using those, and you call this method, then it will have side effects like
- // creating game objects
- // Otherwise, it should be safe to call since all the fake instances will be limited to
- // within a cloned copy of the DiContainer and should not have any side effects
- public void Validate()
- {
- var container = CloneForValidate();
- Assert.That(container.IsValidating);
- // It's tempting here to iterate over all the BindingId's in _providers
- // and make sure they can be resolved but that fails once we start
- // using complex conditionals, so this is the best we can do
- container.ResolveDependencyRoots();
- container.ValidateIValidatables();
- }
- public List<object> ResolveDependencyRoots()
- {
- var context = new InjectContext(
- this, typeof(object), DependencyRootIdentifier);
- context.SourceType = InjectSources.Local;
- context.Optional = true;
- return ResolveAll(context).Cast<object>().ToList();
- }
- DiContainer CloneForValidate()
- {
- FlushBindings();
- DiContainer container;
- if (this.ParentContainer == null)
- {
- container = new DiContainer(null, true);
- }
- else
- {
- // Need to clone all parents too
- container = new DiContainer(
- this.ParentContainer.CloneForValidate(), true);
- }
- // Validating shouldn't have side effects, so assert if this occurs
- container.AssertOnNewGameObjects = true;
- foreach (var binding in _processedBindings)
- {
- container._currentBindings.Enqueue(binding);
- }
- container.FlushBindings();
- return container;
- }
- public void ValidateIValidatables()
- {
- Assert.That(IsValidating);
- foreach (var pair in _providers.ToList())
- {
- var bindingId = pair.Key;
- var providers = pair.Value;
- // Validate all IValidatable's
- List<ProviderInfo> validatableProviders;
- var injectContext = new InjectContext(
- this, bindingId.Type, bindingId.Identifier);
- if (bindingId.Type.DerivesFrom<IValidatable>())
- {
- validatableProviders = providers;
- }
- else
- {
- validatableProviders = providers
- .Where(x => x.Provider.GetInstanceType(injectContext)
- .DerivesFrom<IValidatable>()).ToList();
- }
- foreach (var provider in validatableProviders)
- {
- var validatable = (IValidatable)provider.Provider.GetInstance(injectContext);
- validatable.Validate();
- }
- }
- }
- public DiContainer CreateSubContainer()
- {
- return CreateSubContainer(_isValidating);
- }
- public DiContainer CreateSubContainer(bool isValidating)
- {
- return new DiContainer(this, isValidating);
- }
- public void RegisterProvider(
- BindingId bindingId, BindingCondition condition, IProvider provider)
- {
- var info = new ProviderInfo(provider, condition);
- if (_providers.ContainsKey(bindingId))
- {
- _providers[bindingId].Add(info);
- }
- else
- {
- _providers.Add(bindingId, new List<ProviderInfo> { info });
- }
- }
- // Wrap IEnumerable<> to avoid LINQ mistakes
- internal List<IProvider> GetAllProviderMatches(InjectContext context)
- {
- Assert.IsNotNull(context);
- return GetProviderMatchesInternal(context).Select(x => x.ProviderInfo.Provider).ToList();
- }
- // Be careful with this method since it is a coroutine
- IEnumerable<ProviderPair> GetProviderMatchesInternal(InjectContext context)
- {
- Assert.IsNotNull(context);
- return GetProvidersForContract(context.GetBindingId(), context.SourceType)
- .Where(x => x.ProviderInfo.Condition == null || x.ProviderInfo.Condition(context));
- }
- IEnumerable<ProviderPair> GetProvidersForContract(BindingId bindingId, InjectSources sourceType)
- {
- FlushBindings();
- switch (sourceType)
- {
- case InjectSources.Local:
- {
- return GetLocalProviders(bindingId).Select(x => new ProviderPair(x, this));
- }
- case InjectSources.Any:
- {
- var localPairs = GetLocalProviders(bindingId).Select(x => new ProviderPair(x, this));
- if (_parentContainer == null)
- {
- return localPairs;
- }
- return localPairs.Concat(
- _parentContainer.GetProvidersForContract(bindingId, InjectSources.Any));
- }
- case InjectSources.AnyParent:
- {
- if (_parentContainer == null)
- {
- return Enumerable.Empty<ProviderPair>();
- }
- return _parentContainer.GetProvidersForContract(bindingId, InjectSources.Any);
- }
- case InjectSources.Parent:
- {
- if (_parentContainer == null)
- {
- return Enumerable.Empty<ProviderPair>();
- }
- return _parentContainer.GetProvidersForContract(bindingId, InjectSources.Local);
- }
- }
- throw Assert.CreateException("Invalid source type");
- }
- List<ProviderInfo> GetLocalProviders(BindingId bindingId)
- {
- List<ProviderInfo> localProviders;
- if (_providers.TryGetValue(bindingId, out localProviders))
- {
- return localProviders;
- }
- // If we are asking for a List<int>, we should also match for any localProviders that are bound to the open generic type List<>
- // Currently it only matches one and not the other - not totally sure if this is better than returning both
- if (bindingId.Type.IsGenericType() && _providers.TryGetValue(new BindingId(bindingId.Type.GetGenericTypeDefinition(), bindingId.Identifier), out localProviders))
- {
- return localProviders;
- }
- return new List<ProviderInfo>();
- }
- // See comment in IResolver.cs for description of this method
- public IList ResolveAll(InjectContext context)
- {
- Assert.IsNotNull(context);
- // Note that different types can map to the same provider (eg. a base type to a concrete class and a concrete class to itself)
- FlushBindings();
- CheckForInstallWarning(context);
- var matches = GetProviderMatchesInternal(context).ToList();
- if (matches.Any())
- {
- var instances = matches.SelectMany(x => SafeGetInstances(x.ProviderInfo.Provider, context)).ToArray();
- if (IsValidating)
- {
- instances = instances.Select(x => x is ValidationMarker ? context.MemberType.GetDefaultValue() : x).ToArray();
- }
- return ReflectionUtil.CreateGenericList(context.MemberType, instances);
- }
- Assert.That(context.Optional,
- "Could not find required dependency with type '{0}' \nObject graph:\n {1}", context.MemberType.Name(), context.GetObjectGraphString());
- return ReflectionUtil.CreateGenericList(context.MemberType, new object[] {});
- }
- void CheckForInstallWarning(InjectContext context)
- {
- Assert.IsNotNull(context);
- #if DEBUG || UNITY_EDITOR
- if (!_isInstalling)
- {
- return;
- }
- if (_hasDisplayedInstallWarning)
- {
- return;
- }
- if (context == null)
- {
- // No way to tell whether this is ok or not so just assume ok
- return;
- }
- var rootContext = context.ParentContextsAndSelf.Last();
- if (rootContext.MemberType.DerivesFrom<IInstaller>())
- {
- // Resolving/instantiating/injecting installers is valid during install phase
- return;
- }
- #if !NOT_UNITY3D
- if (rootContext.MemberType.DerivesFrom<DecoratorInstaller>())
- {
- return;
- }
- #endif
- _hasDisplayedInstallWarning = true;
- // Feel free to comment this out if you are comfortable with this practice
- Log.Warn("Zenject Warning: It is bad practice to call Inject/Resolve/Instantiate before all the Installers have completed! This is important to ensure that all bindings have properly been installed in case they are needed when injecting/instantiating/resolving. Detected when operating on type '{0}'", rootContext.MemberType.Name());
- #endif
- }
- // See comment in IResolver.cs for description of this method
- public List<Type> ResolveTypeAll(InjectContext context)
- {
- Assert.IsNotNull(context);
- FlushBindings();
- CheckForInstallWarning(context);
- var providers = GetProviderMatchesInternal(context).ToList();
- if (providers.Count > 0 )
- {
- return providers.Select(x => x.ProviderInfo.Provider.GetInstanceType(context)).Where(x => x != null).ToList();
- }
- return new List<Type> {};
- }
- // Try looking up a single provider for a given context
- // Note that this method should not throw zenject exceptions
- internal ProviderLookupResult TryGetUniqueProvider(
- InjectContext context, out IProvider provider)
- {
- Assert.IsNotNull(context);
- // Note that different types can map to the same provider (eg. a base type to a concrete class and a concrete class to itself)
- var providers = GetProviderMatchesInternal(context).ToList();
- if (providers.IsEmpty())
- {
- provider = null;
- return ProviderLookupResult.None;
- }
- if (providers.Count > 1)
- {
- // If we find multiple providers and we are looking for just one, then
- // try to intelligently choose one from the list before giving up
- // First try picking the most 'local' dependencies
- // This will bias towards bindings for the lower level specific containers rather than the global high level container
- // This will, for example, allow you to just ask for a DiContainer dependency without needing to specify [Inject(Source = InjectSources.Local)]
- // (otherwise it would always match for a list of DiContainer's for all parent containers)
- var sortedProviders = providers.Select(x => new { Pair = x, Distance = GetContainerHeirarchyDistance(x.Container) }).OrderBy(x => x.Distance).ToList();
- sortedProviders.RemoveAll(x => x.Distance != sortedProviders[0].Distance);
- if (sortedProviders.Count == 1)
- {
- // We have one match that is the closest
- provider = sortedProviders[0].Pair.ProviderInfo.Provider;
- }
- else
- {
- // Try choosing the one with a condition before giving up and throwing an exception
- // This is nice because it allows us to bind a default and then override with conditions
- provider = sortedProviders.Where(x => x.Pair.ProviderInfo.Condition != null).Select(x => x.Pair.ProviderInfo.Provider).OnlyOrDefault();
- if (provider == null)
- {
- return ProviderLookupResult.Multiple;
- }
- }
- }
- else
- {
- provider = providers.Single().ProviderInfo.Provider;
- }
- Assert.IsNotNull(provider);
- return ProviderLookupResult.Success;
- }
- // See comment in IResolver.cs for description of this method
- public object Resolve(InjectContext context)
- {
- Assert.IsNotNull(context);
- IProvider provider;
- FlushBindings();
- CheckForInstallWarning(context);
- var result = TryGetUniqueProvider(context, out provider);
- Assert.That(result != ProviderLookupResult.Multiple,
- "Found multiple matches when only one was expected for type '{0}'{1}. \nObject graph:\n {2}",
- context.MemberType.Name(),
- (context.ObjectType == null ? "" : " while building object with type '{0}'".Fmt(context.ObjectType.Name())),
- context.GetObjectGraphString());
- if (result == ProviderLookupResult.None)
- {
- // If it's a generic list then try matching multiple instances to its generic type
- if (ReflectionUtil.IsGenericList(context.MemberType))
- {
- var subType = context.MemberType.GenericArguments().Single();
- var subContext = context.Clone();
- subContext.MemberType = subType;
- return ResolveAll(subContext);
- }
- if (context.Optional)
- {
- return context.FallBackValue;
- }
- throw Assert.CreateException("Unable to resolve type '{0}'{1}. \nObject graph:\n{2}",
- context.MemberType.Name() + (context.Identifier == null ? "" : " with ID '{0}'".Fmt(context.Identifier.ToString())),
- (context.ObjectType == null ? "" : " while building object with type '{0}'".Fmt(context.ObjectType.Name())),
- context.GetObjectGraphString());
- }
- Assert.That(result == ProviderLookupResult.Success);
- Assert.IsNotNull(provider);
- var instances = SafeGetInstances(provider, context);
- Assert.That(!instances.IsEmpty(), "Provider returned zero instances when one was expected!");
- Assert.That(instances.Count() == 1, "Provider returned multiple instances when one was expected!");
- return instances.First();
- }
- IEnumerable<object> SafeGetInstances(IProvider provider, InjectContext context)
- {
- Assert.IsNotNull(context);
- if (ChecksForCircularDependencies)
- {
- var lookupId = new LookupId(provider, context.GetBindingId());
- // Allow one before giving up so that you can do circular dependencies via postinject or fields
- Assert.That(_resolvesInProgress.Where(x => x.Equals(lookupId)).Count() <= 1,
- "Circular dependency detected! \nObject graph:\n {0}", context.GetObjectGraphString());
- _resolvesInProgress.Push(lookupId);
- try
- {
- return provider.GetAllInstances(context);
- }
- finally
- {
- Assert.That(_resolvesInProgress.Peek().Equals(lookupId));
- _resolvesInProgress.Pop();
- }
- }
- else
- {
- return provider.GetAllInstances(context);
- }
- }
- int GetContainerHeirarchyDistance(DiContainer container)
- {
- return GetContainerHeirarchyDistance(container, 0);
- }
- int GetContainerHeirarchyDistance(DiContainer container, int depth)
- {
- if (container == this)
- {
- return depth;
- }
- Assert.IsNotNull(_parentContainer);
- return _parentContainer.GetContainerHeirarchyDistance(container, depth + 1);
- }
- public IEnumerable<Type> GetDependencyContracts<TContract>()
- {
- return GetDependencyContracts(typeof(TContract));
- }
- public IEnumerable<Type> GetDependencyContracts(Type contract)
- {
- FlushBindings();
- foreach (var injectMember in TypeAnalyzer.GetInfo(contract).AllInjectables)
- {
- yield return injectMember.MemberType;
- }
- }
- public T InstantiateExplicit<T>(List<TypeValuePair> extraArgs)
- {
- return (T)InstantiateExplicit(typeof(T), extraArgs);
- }
- public object InstantiateExplicit(Type concreteType, List<TypeValuePair> extraArgs)
- {
- bool autoInject = true;
- return InstantiateExplicit(
- concreteType,
- autoInject,
- new InjectArgs()
- {
- ExtraArgs = extraArgs,
- Context = new InjectContext(this, concreteType, null),
- ConcreteIdentifier = null,
- UseAllArgs = true,
- });
- }
- // See comment in IInstantiator.cs for description of this method
- public object InstantiateExplicit(Type concreteType, bool autoInject, InjectArgs args)
- {
- #if PROFILING_ENABLED
- using (ProfileBlock.Start("Zenject.Instantiate({0})", concreteType))
- #endif
- {
- return InstantiateInternal(concreteType, autoInject, args);
- }
- }
- public static bool CanCreateOrInjectDuringValidation(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
- return type.DerivesFrom<IInstaller>()
- || type.DerivesFrom<IValidatable>()
- #if !NOT_UNITY3D
- || type.DerivesFrom<Context>()
- || type.DerivesFrom<DecoratorInstaller>()
- #endif
- #if !(UNITY_WSA && ENABLE_DOTNET)
- || type.HasAttribute<ZenjectAllowDuringValidationAttribute>()
- #endif
- ;
- }
- object InstantiateInternal(Type concreteType, bool autoInject, InjectArgs args)
- {
- #if !NOT_UNITY3D
- Assert.That(!concreteType.DerivesFrom<UnityEngine.Component>(),
- "Error occurred while instantiating object of type '{0}'. Instantiator should not be used to create new mono behaviours. Must use InstantiatePrefabForComponent, InstantiatePrefab, or InstantiateComponent.", concreteType.Name());
- #endif
- Assert.That(!concreteType.IsAbstract(), "Expected type 'type' to be non-abstract", concreteType);
- FlushBindings();
- CheckForInstallWarning(args.Context);
- var typeInfo = TypeAnalyzer.GetInfo(concreteType);
- object newObj;
- #if !NOT_UNITY3D
- if (concreteType.DerivesFrom<ScriptableObject>())
- {
- Assert.That( typeInfo.ConstructorInjectables.IsEmpty(),
- "Found constructor parameters on ScriptableObject type '{0}'. This is not allowed. Use an [Inject] method or fields instead.");
- if (!IsValidating || CanCreateOrInjectDuringValidation(concreteType))
- {
- newObj = ScriptableObject.CreateInstance(concreteType);
- }
- else
- {
- newObj = new ValidationMarker(concreteType);
- }
- }
- else
- #endif
- {
- Assert.IsNotNull(typeInfo.InjectConstructor,
- "More than one (or zero) constructors found for type '{0}' when creating dependencies. Use one [Inject] attribute to specify which to use.", concreteType);
- // Make a copy since we remove from it below
- var paramValues = new List<object>();
- foreach (var injectInfo in typeInfo.ConstructorInjectables)
- {
- object value;
- if (!InjectUtil.PopValueWithType(
- args.ExtraArgs, injectInfo.MemberType, out value))
- {
- value = Resolve(injectInfo.CreateInjectContext(
- this, args.Context, null, args.ConcreteIdentifier));
- }
- if (value is ValidationMarker)
- {
- Assert.That(IsValidating);
- paramValues.Add(injectInfo.MemberType.GetDefaultValue());
- }
- else
- {
- paramValues.Add(value);
- }
- }
- if (!IsValidating || CanCreateOrInjectDuringValidation(concreteType))
- {
- //Log.Debug("Zenject: Instantiating type '{0}'", concreteType.Name());
- try
- {
- #if PROFILING_ENABLED
- using (ProfileBlock.Start("{0}.{0}()", concreteType))
- #endif
- {
- newObj = typeInfo.InjectConstructor.Invoke(paramValues.ToArray());
- }
- }
- catch (Exception e)
- {
- throw Assert.CreateException(
- e, "Error occurred while instantiating object with type '{0}'", concreteType.Name());
- }
- }
- else
- {
- newObj = new ValidationMarker(concreteType);
- }
- }
- if (autoInject)
- {
- InjectExplicit(newObj, concreteType, args);
- }
- else if (args.UseAllArgs && !args.ExtraArgs.IsEmpty())
- {
- throw Assert.CreateException(
- "Passed unnecessary parameters when injecting into type '{0}'. \nExtra Parameters: {1}\nObject graph:\n{2}",
- newObj.GetType().Name(), String.Join(",", args.ExtraArgs.Select(x => x.Type.Name()).ToArray()), args.Context.GetObjectGraphString());
- }
- return newObj;
- }
- public void InjectExplicit(object injectable, List<TypeValuePair> extraArgs)
- {
- Type injectableType;
- if (injectable is ValidationMarker)
- {
- injectableType = ((ValidationMarker)injectable).MarkedType;
- }
- else
- {
- injectableType = injectable.GetType();
- }
- InjectExplicit(
- injectable,
- injectableType,
- new InjectArgs()
- {
- ExtraArgs = extraArgs,
- UseAllArgs = true,
- Context = new InjectContext(this, injectableType, null),
- ConcreteIdentifier = null,
- });
- }
- // See comment in IResolver.cs for description of this method
- public void InjectExplicit(
- object injectable, Type injectableType, InjectArgs args)
- {
- Assert.That(injectable != null);
- // Installers are the only things that we instantiate/inject on during validation
- bool isDryRun = IsValidating && !CanCreateOrInjectDuringValidation(injectableType);
- if (!isDryRun)
- {
- Assert.IsEqual(injectable.GetType(), injectableType);
- }
- #if !NOT_UNITY3D
- Assert.That(injectableType != typeof(GameObject),
- "Use InjectGameObject to Inject game objects instead of Inject method");
- #endif
- FlushBindings();
- CheckForInstallWarning(args.Context);
- var typeInfo = TypeAnalyzer.GetInfo(injectableType);
- foreach (var injectInfo in typeInfo.FieldInjectables.Concat(
- typeInfo.PropertyInjectables))
- {
- object value;
- if (InjectUtil.PopValueWithType(args.ExtraArgs, injectInfo.MemberType, out value))
- {
- if (!isDryRun)
- {
- if (value is ValidationMarker)
- {
- Assert.That(IsValidating);
- }
- else
- {
- injectInfo.Setter(injectable, value);
- }
- }
- }
- else
- {
- value = Resolve(
- injectInfo.CreateInjectContext(
- this, args.Context, injectable, args.ConcreteIdentifier));
- if (injectInfo.Optional && value == null)
- {
- // Do not override in this case so it retains the hard-coded value
- }
- else
- {
- if (!isDryRun)
- {
- if (value is ValidationMarker)
- {
- Assert.That(IsValidating);
- }
- else
- {
- injectInfo.Setter(injectable, value);
- }
- }
- }
- }
- }
- foreach (var method in typeInfo.PostInjectMethods)
- {
- #if PROFILING_ENABLED
- using (ProfileBlock.Start("{0}.{1}()", injectableType, method.MethodInfo.Name))
- #endif
- {
- var paramValues = new List<object>();
- foreach (var injectInfo in method.InjectableInfo)
- {
- object value;
- if (!InjectUtil.PopValueWithType(args.ExtraArgs, injectInfo.MemberType, out value))
- {
- value = Resolve(
- injectInfo.CreateInjectContext(this, args.Context, injectable, args.ConcreteIdentifier));
- }
- if (value is ValidationMarker)
- {
- Assert.That(IsValidating);
- paramValues.Add(injectInfo.MemberType.GetDefaultValue());
- }
- else
- {
- paramValues.Add(value);
- }
- }
- if (!isDryRun)
- {
- method.MethodInfo.Invoke(injectable, paramValues.ToArray());
- }
- }
- }
- if (args.UseAllArgs && !args.ExtraArgs.IsEmpty())
- {
- throw Assert.CreateException(
- "Passed unnecessary parameters when injecting into type '{0}'. \nExtra Parameters: {1}\nObject graph:\n{2}",
- injectableType.Name(), String.Join(",", args.ExtraArgs.Select(x => x.Type.Name()).ToArray()), args.Context.GetObjectGraphString());
- }
- }
- #if !NOT_UNITY3D
- public GameObject InstantiatePrefabResourceExplicit(
- string resourcePath, List<TypeValuePair> extraArgs)
- {
- return InstantiatePrefabResourceExplicit(
- resourcePath, extraArgs, null);
- }
- public GameObject InstantiatePrefabResourceExplicit(
- string resourcePath, List<TypeValuePair> extraArgs, string groupName)
- {
- return InstantiatePrefabResourceExplicit(
- resourcePath, extraArgs, groupName, true);
- }
- public GameObject InstantiatePrefabResourceExplicit(
- string resourcePath, List<TypeValuePair> extraArgs,
- string groupName, bool useAllArgs)
- {
- var prefab = (GameObject)Resources.Load(resourcePath);
- Assert.IsNotNull(prefab, "Could not find prefab at resource location '{0}'".Fmt(resourcePath));
- return InstantiatePrefabExplicit(
- prefab, extraArgs, groupName, useAllArgs);
- }
- public GameObject InstantiatePrefabExplicit(
- UnityEngine.Object prefab, List<TypeValuePair> extraArgs)
- {
- return InstantiatePrefabExplicit(
- prefab, extraArgs, null);
- }
- public GameObject InstantiatePrefabExplicit(
- UnityEngine.Object prefab, List<TypeValuePair> extraArgs,
- string groupName)
- {
- return InstantiatePrefabExplicit(
- prefab, extraArgs, groupName, true);
- }
- public GameObject InstantiatePrefabExplicit(
- UnityEngine.Object prefab, List<TypeValuePair> extraArgs,
- string groupName, bool useAllArgs)
- {
- FlushBindings();
- var gameObj = CreateAndParentPrefab(prefab, groupName);
- InjectGameObjectExplicit(
- gameObj, true, extraArgs, useAllArgs);
- return gameObj;
- }
- // Don't use this unless you know what you're doing
- // You probably want to use InstantiatePrefab instead
- // This one will only create the prefab and will not inject into it
- public GameObject CreateAndParentPrefabResource(string resourcePath)
- {
- return CreateAndParentPrefabResource(resourcePath, null);
- }
- // Don't use this unless you know what you're doing
- // You probably want to use InstantiatePrefab instead
- // This one will only create the prefab and will not inject into it
- public GameObject CreateAndParentPrefabResource(string resourcePath, string groupName)
- {
- var prefab = (GameObject)Resources.Load(resourcePath);
- Assert.IsNotNull(prefab,
- "Could not find prefab at resource location '{0}'".Fmt(resourcePath));
- return CreateAndParentPrefab(prefab, groupName);
- }
- GameObject GetPrefabAsGameObject(UnityEngine.Object prefab)
- {
- if (prefab is GameObject)
- {
- return (GameObject)prefab;
- }
- Assert.That(prefab is Component, "Invalid type given for prefab. Given object name: '{0}'", prefab.name);
- return ((Component)prefab).gameObject;
- }
- // Don't use this unless you know what you're doing
- // You probably want to use InstantiatePrefab instead
- // This one will only create the prefab and will not inject into it
- public GameObject CreateAndParentPrefab(UnityEngine.Object prefab, string groupName)
- {
- Assert.That(!AssertOnNewGameObjects,
- "Given DiContainer does not support creating new game objects");
- FlushBindings();
- var prefabAsGameObject = GetPrefabAsGameObject(prefab);
- GameObject gameObj;
- if (IsValidating)
- {
- // We need to avoid triggering any Awake() method during validation
- // so temporarily disable the prefab so that the object gets
- // instantiated as disabled
- bool wasActive = prefabAsGameObject.activeSelf;
- prefabAsGameObject.SetActive(false);
- try
- {
- gameObj = (GameObject)GameObject.Instantiate(prefabAsGameObject);
- }
- finally
- {
- prefabAsGameObject.SetActive(wasActive);
- }
- }
- else
- {
- gameObj = (GameObject)GameObject.Instantiate(prefabAsGameObject);
- }
- gameObj.transform.SetParent(GetTransformGroup(groupName), false);
- return gameObj;
- }
- public GameObject CreateEmptyGameObject(string name)
- {
- return CreateEmptyGameObject(name, null);
- }
- public GameObject CreateEmptyGameObject(string name, string groupName)
- {
- Assert.That(!AssertOnNewGameObjects,
- "Given DiContainer does not support creating new game objects");
- FlushBindings();
- var gameObj = new GameObject(name);
- gameObj.transform.SetParent(GetTransformGroup(groupName), false);
- return gameObj;
- }
- public T InstantiatePrefabForComponentExplicit<T>(
- UnityEngine.Object prefab, List<TypeValuePair> extraArgs)
- {
- return (T)InstantiatePrefabForComponentExplicit(
- typeof(T), prefab, extraArgs);
- }
- // Note: Any arguments that are used will be removed from extraArgs
- public object InstantiatePrefabForComponentExplicit(
- Type componentType, UnityEngine.Object prefab, List<TypeValuePair> extraArgs)
- {
- return InstantiatePrefabForComponentExplicit(
- componentType, prefab, extraArgs, null);
- }
- public object InstantiatePrefabForComponentExplicit(
- Type componentType, UnityEngine.Object prefab, List<TypeValuePair> extraArgs,
- string groupName)
- {
- return InstantiatePrefabForComponentExplicit(
- componentType, prefab, groupName,
- new InjectArgs()
- {
- ExtraArgs = extraArgs,
- Context = new InjectContext(this, componentType, null),
- ConcreteIdentifier = null,
- UseAllArgs = true,
- });
- }
- // Note: Any arguments that are used will be removed from extraArgs
- public object InstantiatePrefabForComponentExplicit(
- Type componentType, UnityEngine.Object prefab, string groupName, InjectArgs args)
- {
- Assert.That(!AssertOnNewGameObjects,
- "Given DiContainer does not support creating new game objects");
- FlushBindings();
- Assert.That(prefab != null, "Null prefab found when instantiating game object");
- Assert.That(componentType.IsInterface() || componentType.DerivesFrom<Component>(),
- "Expected type '{0}' to derive from UnityEngine.Component", componentType.Name());
- var gameObj = (GameObject)GameObject.Instantiate(GetPrefabAsGameObject(prefab));
- gameObj.transform.SetParent(GetTransformGroup(groupName), false);
- gameObj.SetActive(true);
- return InjectGameObjectForComponentExplicit(
- gameObj, componentType, args);
- }
- Transform GetTransformGroup(string groupName)
- {
- Assert.That(!AssertOnNewGameObjects,
- "Given DiContainer does not support creating new game objects");
- if (DefaultParent == null)
- {
- if (groupName == null)
- {
- return null;
- }
- return (GameObject.Find("/" + groupName) ?? new GameObject(groupName)).transform;
- }
- if (groupName == null)
- {
- return DefaultParent;
- }
- foreach (Transform child in DefaultParent)
- {
- if (child.name == groupName)
- {
- return child;
- }
- }
- var group = new GameObject(groupName).transform;
- group.SetParent(DefaultParent, false);
- return group;
- }
- #endif
- public T Instantiate<T>()
- {
- return Instantiate<T>(new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public T Instantiate<T>(IEnumerable<object> extraArgs)
- {
- var result = Instantiate(typeof(T), extraArgs);
- if (IsValidating && !(result is T))
- {
- Assert.That(result is ValidationMarker);
- return default(T);
- }
- return (T)result;
- }
- public object Instantiate(Type concreteType)
- {
- return Instantiate(concreteType, new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public object Instantiate(
- Type concreteType, IEnumerable<object> extraArgs)
- {
- Assert.That(!extraArgs.ContainsItem(null),
- "Null value given to factory constructor arguments when instantiating object with type '{0}'. In order to use null use InstantiateExplicit", concreteType);
- return InstantiateExplicit(
- concreteType, InjectUtil.CreateArgList(extraArgs));
- }
- #if !NOT_UNITY3D
- // See comment in IInstantiator.cs for description of this method
- public TContract InstantiateComponent<TContract>(GameObject gameObject)
- where TContract : Component
- {
- return InstantiateComponent<TContract>(gameObject, new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public TContract InstantiateComponent<TContract>(
- GameObject gameObject, IEnumerable<object> extraArgs)
- where TContract : Component
- {
- return (TContract)InstantiateComponent(typeof(TContract), gameObject, extraArgs);
- }
- public Component InstantiateComponent(
- Type componentType, GameObject gameObject)
- {
- return InstantiateComponent(componentType, gameObject, new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public Component InstantiateComponent(
- Type componentType, GameObject gameObject, IEnumerable<object> extraArgs)
- {
- return InstantiateComponentExplicit(
- componentType, gameObject, InjectUtil.CreateArgList(extraArgs));
- }
- public Component InstantiateComponentExplicit(
- Type componentType, GameObject gameObject, List<TypeValuePair> extraArgs)
- {
- Assert.That(componentType.DerivesFrom<Component>());
- FlushBindings();
- var monoBehaviour = (Component)gameObject.AddComponent(componentType);
- InjectExplicit(monoBehaviour, extraArgs);
- return monoBehaviour;
- }
- public GameObject InstantiatePrefab(UnityEngine.Object prefab)
- {
- return InstantiatePrefab(prefab, new object[0]);
- }
- public GameObject InstantiatePrefab(
- UnityEngine.Object prefab, IEnumerable<object> extraArgs)
- {
- return InstantiatePrefab(
- prefab, extraArgs, null);
- }
- public GameObject InstantiatePrefab(
- UnityEngine.Object prefab, IEnumerable<object> extraArgs, string groupName)
- {
- return InstantiatePrefabExplicit(
- prefab, InjectUtil.CreateArgList(extraArgs),
- groupName);
- }
- // See comment in IInstantiator.cs for description of this method
- public GameObject InstantiatePrefabResource(string resourcePath)
- {
- return InstantiatePrefabResource(resourcePath, new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public GameObject InstantiatePrefabResource(
- string resourcePath, IEnumerable<object> extraArgs)
- {
- return InstantiatePrefabResource(
- resourcePath, extraArgs, null);
- }
- // See comment in IInstantiator.cs for description of this method
- public GameObject InstantiatePrefabResource(
- string resourcePath, IEnumerable<object> extraArgs, string groupName)
- {
- return InstantiatePrefabResourceExplicit(
- resourcePath, InjectUtil.CreateArgList(extraArgs),
- groupName);
- }
- /////////////// InstantiatePrefabForComponent
- public T InstantiatePrefabForComponent<T>(UnityEngine.Object prefab)
- {
- return InstantiatePrefabForComponent<T>(prefab, new object[0]);
- }
- public T InstantiatePrefabForComponent<T>(
- UnityEngine.Object prefab, IEnumerable<object> extraArgs)
- {
- return (T)InstantiatePrefabForComponent(
- typeof(T), prefab, extraArgs);
- }
- public object InstantiatePrefabForComponent(
- Type concreteType, UnityEngine.Object prefab, IEnumerable<object> extraArgs)
- {
- return InstantiatePrefabForComponentExplicit(
- concreteType, prefab,
- InjectUtil.CreateArgList(extraArgs));
- }
- /////////////// InstantiatePrefabForComponent
- // See comment in IInstantiator.cs for description of this method
- public T InstantiatePrefabResourceForComponent<T>(string resourcePath)
- {
- return InstantiatePrefabResourceForComponent<T>(resourcePath, new object[0]);
- }
- // See comment in IInstantiator.cs for description of this method
- public T InstantiatePrefabResourceForComponent<T>(
- string resourcePath, IEnumerable<object> extraArgs)
- {
- return (T)InstantiatePrefabResourceForComponent(typeof(T), resourcePath, extraArgs);
- }
- // See comment in IInstantiator.cs for description of this method
- public object InstantiatePrefabResourceForComponent(
- Type concreteType, string resourcePath, IEnumerable<object> extraArgs)
- {
- Assert.That(!extraArgs.ContainsItem(null),
- "Null value given to factory constructor arguments when instantiating object with type '{0}'. In order to use null use InstantiatePrefabForComponentExplicit", concreteType);
- return InstantiatePrefabResourceForComponentExplicit(
- concreteType, resourcePath, InjectUtil.CreateArgList(extraArgs));
- }
- public T InstantiatePrefabResourceForComponentExplicit<T>(
- string resourcePath, List<TypeValuePair> extraArgs)
- {
- return (T)InstantiatePrefabResourceForComponentExplicit(
- typeof(T), resourcePath, extraArgs);
- }
- // Note: Any arguments that are used will be removed from extraArgs
- public object InstantiatePrefabResourceForComponentExplicit(
- Type componentType, string resourcePath, List<TypeValuePair> extraArgs)
- {
- return InstantiatePrefabResourceForComponentExplicit(
- componentType, resourcePath, null,
- new InjectArgs()
- {
- ExtraArgs = extraArgs,
- Context = new InjectContext(this, componentType, null),
- ConcreteIdentifier = null,
- UseAllArgs = true,
- });
- }
- // Note: Any arguments that are used will be removed from extraArgs
- public object InstantiatePrefabResourceForComponentExplicit(
- Type componentType, string resourcePath, string groupName, InjectArgs args)
- {
- var prefab = (GameObject)Resources.Load(resourcePath);
- Assert.IsNotNull(prefab,
- "Could not find prefab at resource location '{0}'".Fmt(resourcePath));
- return InstantiatePrefabForComponentExpli