using System; using System.Collections.Generic; using System.Linq; using System.Text; using ModestTree; using Zenject.Internal; namespace Zenject { [NoReflectionBaking] public class InjectContext : IDisposable { BindingId _bindingId; Type _objectType; InjectContext _parentContext; object _objectInstance; string _memberName; bool _optional; InjectSources _sourceType; object _fallBackValue; object _concreteIdentifier; DiContainer _container; public InjectContext() { _bindingId = new BindingId(); Reset(); } public InjectContext(DiContainer container, Type memberType) : this() { Container = container; MemberType = memberType; } public InjectContext(DiContainer container, Type memberType, object identifier) : this(container, memberType) { Identifier = identifier; } public InjectContext(DiContainer container, Type memberType, object identifier, bool optional) : this(container, memberType, identifier) { Optional = optional; } public void Dispose() { ZenPools.DespawnInjectContext(this); } public void Reset() { _objectType = null; _parentContext = null; _objectInstance = null; _memberName = ""; _optional = false; _sourceType = InjectSources.Any; _fallBackValue = null; _container = null; _bindingId.Type = null; _bindingId.Identifier = null; } public BindingId BindingId { get { return _bindingId; } } // The type of the object which is having its members injected // NOTE: This is null for root calls to Resolve<> or Instantiate<> public Type ObjectType { get { return _objectType; } set { _objectType = value; } } // Parent context that triggered the creation of ObjectType // This can be used for very complex conditions using parent info such as identifiers, types, etc. // Note that ParentContext.MemberType is not necessarily the same as ObjectType, // since the ObjectType could be a derived type from ParentContext.MemberType public InjectContext ParentContext { get { return _parentContext; } set { _parentContext = value; } } // The instance which is having its members injected // Note that this is null when injecting into the constructor public object ObjectInstance { get { return _objectInstance; } set { _objectInstance = value; } } // Identifier - most of the time this is null // It will match 'foo' in this example: // ... In an installer somewhere: // Container.Bind("foo").AsSingle(); // ... // ... In a constructor: // public Foo([Inject(Id = "foo") Foo foo) public object Identifier { get { return _bindingId.Identifier; } set { _bindingId.Identifier = value; } } // The constructor parameter name, or field name, or property name public string MemberName { get { return _memberName; } set { _memberName = value; } } // The type of the constructor parameter, field or property public Type MemberType { get { return _bindingId.Type; } set { _bindingId.Type = value; } } // When optional, null is a valid value to be returned public bool Optional { get { return _optional; } set { _optional = value; } } // When set to true, this will only look up dependencies in the local container and will not // search in parent containers public InjectSources SourceType { get { return _sourceType; } set { _sourceType = value; } } public object ConcreteIdentifier { get { return _concreteIdentifier; } set { _concreteIdentifier = value; } } // When optional, this is used to provide the value public object FallBackValue { get { return _fallBackValue; } set { _fallBackValue = value; } } // The container used for this injection public DiContainer Container { get { return _container; } set { _container = value; } } public IEnumerable ParentContexts { get { if (ParentContext == null) { yield break; } yield return ParentContext; foreach (var context in ParentContext.ParentContexts) { yield return context; } } } public IEnumerable ParentContextsAndSelf { get { yield return this; foreach (var context in ParentContexts) { yield return context; } } } // This will return the types of all the objects that are being injected // So if you have class Foo which has constructor parameter of type IBar, // and IBar resolves to Bar, this will be equal to (Bar, Foo) public IEnumerable AllObjectTypes { get { foreach (var context in ParentContextsAndSelf) { if (context.ObjectType != null) { yield return context.ObjectType; } } } } public InjectContext CreateSubContext(Type memberType) { return CreateSubContext(memberType, null); } public InjectContext CreateSubContext(Type memberType, object identifier) { var subContext = new InjectContext(); subContext.ParentContext = this; subContext.Identifier = identifier; subContext.MemberType = memberType; // Clear these subContext.ConcreteIdentifier = null; subContext.MemberName = ""; subContext.FallBackValue = null; // Inherit these ones by default subContext.ObjectType = ObjectType; subContext.ObjectInstance = ObjectInstance; subContext.Optional = Optional; subContext.SourceType = SourceType; subContext.Container = Container; return subContext; } public InjectContext Clone() { var clone = new InjectContext(); clone.ObjectType = ObjectType; clone.ParentContext = ParentContext; clone.ConcreteIdentifier = ConcreteIdentifier; clone.ObjectInstance = ObjectInstance; clone.Identifier = Identifier; clone.MemberType = MemberType; clone.MemberName = MemberName; clone.Optional = Optional; clone.SourceType = SourceType; clone.FallBackValue = FallBackValue; clone.Container = Container; return clone; } // This is very useful to print out for debugging purposes public string GetObjectGraphString() { var result = new StringBuilder(); foreach (var context in ParentContextsAndSelf.Reverse()) { if (context.ObjectType == null) { continue; } result.AppendLine(context.ObjectType.PrettyName()); } return result.ToString(); } } }