diff --git a/Stylet/StyletIoC.cs b/Stylet/StyletIoC.cs index 871be33..1e2b0c5 100644 --- a/Stylet/StyletIoC.cs +++ b/Stylet/StyletIoC.cs @@ -26,6 +26,7 @@ namespace Stylet T Get(string key = null); IEnumerable GetAll(Type type, string key = null); IEnumerable GetAll(string key = null); + void BuildUp(object item); } public interface IStyletIoCBindTo @@ -53,6 +54,7 @@ namespace Stylet private ConcurrentDictionary getAllRegistrations = new ConcurrentDictionary(); // The list object is used for locking it private ConcurrentDictionary> unboundGenerics = new ConcurrentDictionary>(); + private ConcurrentDictionary builderUppers = new ConcurrentDictionary(); private ModuleBuilder factoryBuilder; @@ -166,6 +168,13 @@ namespace Stylet return this.GetAll(typeof(T), key).Cast(); } + public void BuildUp(object item) + { + var builderUpper = this.GetBuilderUpper(item.GetType()); + builderUpper.GetImplementor(this)(item); + } + + private bool CanResolve(TypeKey typeKey) { IRegistrationCollection registrations; @@ -283,7 +292,6 @@ namespace Stylet IRegistrationCollection registrations; // Try to get registrations. If there are none, see if we can add some from unbound generics - // If we still fail, try searching the 'get all' types if (!this.registrations.TryGetValue(typeKey, out registrations) && !this.TryCreateGenericTypesForUnboundGeneric(typeKey, out registrations)) { @@ -431,6 +439,11 @@ namespace Stylet return actualType; } + private BuilderUpper GetBuilderUpper(Type type) + { + return this.builderUppers.GetOrAdd(type, x => new BuilderUpper(type)); + } + #endregion #region BindTo @@ -906,20 +919,22 @@ namespace Stylet this.type = type; } - public Expression GetExpression(StyletIoC container, ParameterExpression inputParameterExpression) + public Expression GetExpression(StyletIoC container, Expression inputParameterExpression) { - var expressions = this.type.GetFields().Select(x => this.ExpressionForMember(container, inputParameterExpression, x, x.FieldType)) - .Concat(this.type.GetProperties().Select(x => this.ExpressionForMember(container, inputParameterExpression, x, x.PropertyType))) + var expressions = this.type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Select(x => this.ExpressionForMember(container, inputParameterExpression, x, x.FieldType)) + .Concat(this.type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Select(x => this.ExpressionForMember(container, inputParameterExpression, x, x.PropertyType))) .Where(x => x != null); // Sadly, we can't cache this expression (I think), as it relies on the inputParameterExpression // which is likely to change between calls // This isn't so bad, so we'll (probably) only need to call this at most twice - once for building up the type on creation, // and once for creating the implemtor (which is used in BuildUp()) + if (!expressions.Any()) + return Expression.Empty(); return Expression.Block(expressions); } - private Expression ExpressionForMember(StyletIoC container, ParameterExpression objExpression, MemberInfo member, Type memberType) + private Expression ExpressionForMember(StyletIoC container, Expression objExpression, MemberInfo member, Type memberType) { var attribute = member.GetCustomAttribute(true); if (attribute == null) @@ -936,8 +951,9 @@ namespace Stylet if (this.implementor != null) return this.implementor; - var parameterExpression = Expression.Parameter(this.type, "inputParameter"); - this.implementor = Expression.Lambda>(this.GetExpression(container, parameterExpression), parameterExpression).Compile(); + var parameterExpression = Expression.Parameter(typeof(object), "inputParameter"); + var typedParameterExpression = Expression.Convert(parameterExpression, this.type); + this.implementor = Expression.Lambda>(this.GetExpression(container, typedParameterExpression), parameterExpression).Compile(); return this.implementor; } } @@ -1024,7 +1040,7 @@ namespace Stylet public StyletIoCCreateFactoryException(string message, Exception innerException) : base(message, innerException) { } } - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class InjectAttribute : Attribute { public InjectAttribute() diff --git a/StyletUnitTests/StyletIoCBuildUpTests.cs b/StyletUnitTests/StyletIoCBuildUpTests.cs new file mode 100644 index 0000000..d4ecf4d --- /dev/null +++ b/StyletUnitTests/StyletIoCBuildUpTests.cs @@ -0,0 +1,121 @@ +using NUnit.Framework; +using Stylet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StyletUnitTests +{ + [TestFixture] + public class StyletIoCBuildUpTests + { + class C1 { } + interface I2 { } + class C2 : I2 { } + + class Subject1 + { + public C1 Ignored; + + [Inject] + public C1 C1; + } + + class Subject2 + { + [Inject] + private C1 c1; + public C1 GetC1() { return this.c1; } + } + + class Subject3 + { + [Inject] + public C1 C1 { get; set; } + } + + class Subject4 + { + [Inject] + public C1 C11 { get; private set; } + [Inject] + private C1 C12 { get; set; } + + public C1 GetC11() { return this.C11; } + public C1 GetC12() { return this.C12; } + } + + class Subject5 + { + [Inject("key")] + public C1 C1; + } + + [Test] + public void BuildsUpPublicFields() + { + var ioc = new StyletIoC(); + ioc.Bind().ToSelf(); + var subject = new Subject1(); + ioc.BuildUp(subject); + + Assert.IsInstanceOf(subject.C1); + Assert.IsNull(subject.Ignored); + } + + [Test] + public void BuildsUpPrivateFields() + { + var ioc = new StyletIoC(); + ioc.Bind().ToSelf(); + var subject = new Subject2(); + ioc.BuildUp(subject); + + Assert.IsInstanceOf(subject.GetC1()); + } + + [Test] + public void BuildsUpPublicProperties() + { + var ioc = new StyletIoC(); + ioc.Bind().ToSelf(); + var subject = new Subject3(); + ioc.BuildUp(subject); + + Assert.IsInstanceOf(subject.C1); + } + + [Test] + public void BuildsUpPrivateProperties() + { + var ioc = new StyletIoC(); + ioc.Bind().ToSelf(); + var subject = new Subject4(); + ioc.BuildUp(subject); + + Assert.IsInstanceOf(subject.GetC11()); + Assert.IsInstanceOf(subject.GetC12()); + } + + [Test] + public void RespectsKeys() + { + var ioc = new StyletIoC(); + ioc.Bind().ToSelf("key"); + var subject = new Subject5(); + ioc.BuildUp(subject); + + Assert.IsInstanceOf(subject.C1); + } + + [Test] + public void ThrowsIfCanNotResolve() + { + var ioc = new StyletIoC(); + var subject = new Subject1(); + Assert.Throws(() => ioc.BuildUp(subject)); + } + } +} diff --git a/StyletUnitTests/StyletUnitTests.csproj b/StyletUnitTests/StyletUnitTests.csproj index 18bb32e..d649631 100644 --- a/StyletUnitTests/StyletUnitTests.csproj +++ b/StyletUnitTests/StyletUnitTests.csproj @@ -48,6 +48,7 @@ +