Split the builder up into separate files, and move a couple of things out to extension methods

This commit is contained in:
Antony Male 2014-09-12 09:50:23 +01:00
parent f9a16df8be
commit 5fd04a410e
30 changed files with 435 additions and 327 deletions

View File

@ -47,15 +47,21 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="StyletIoC\Builder\ICreator.cs" />
<Compile Include="StyletIoC\Builder\IRegistration.cs" />
<Compile Include="StyletIoC\Creation\ICreator.cs" />
<Compile Include="StyletIoC\Creation\IRegistration.cs" />
<Compile Include="StyletIoC\Internal\Builders\AbstractFactoryBinding.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderBindingBase.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderBindTo.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderFactoryBinding.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderToAllImplementationsBinding.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderTypeBinding.cs" />
<Compile Include="StyletIoC\Internal\IRegistrationCollection.cs" />
<Compile Include="StyletIoC\Internal\Creators\AbstractFactoryCreator.cs" />
<Compile Include="StyletIoC\Internal\Creators\CreatorBase.cs" />
<Compile Include="StyletIoC\Internal\Creators\FactoryCreator.cs" />
<Compile Include="StyletIoC\Internal\Creators\TypeCreator.cs" />
<Compile Include="StyletIoC\Internal\DelegatingDictionary.cs" />
<Compile Include="StyletIoC\Builder\IRegistrationContext.cs" />
<Compile Include="StyletIoC\Creation\IRegistrationContext.cs" />
<Compile Include="StyletIoC\Internal\RegistrationCollections\RegistrationCollection.cs" />
<Compile Include="StyletIoC\Internal\RegistrationCollections\SingleRegistration.cs" />
<Compile Include="StyletIoC\Internal\Registrations\FuncNoKeyRegistration.cs" />
@ -113,7 +119,7 @@
<Compile Include="PropertyChangedExtensions.cs" />
<Compile Include="Screen.cs" />
<Compile Include="ScreenExtensions.cs" />
<Compile Include="StyletIoC\Builder\BuilderUpper.cs" />
<Compile Include="StyletIoC\Creation\BuilderUpper.cs" />
<Compile Include="StyletIoC\StyletIoCBuilder.cs" />
<Compile Include="StyletIoC\Internal\Container.cs" />
<Compile Include="StyletIoC\Internal\UnboundGeneric.cs" />
@ -132,6 +138,7 @@
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,11 +1,11 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using StyletIoC.Internal;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace StyletIoC.Builder
namespace StyletIoC.Creation
{
/// <summary>
/// A BuilderUpper knows how to build up an object - that is, populate all parameters decorated with [Inject]

View File

@ -5,7 +5,7 @@ using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Builder
namespace StyletIoC.Creation
{
/// <summary>
/// An ICreator is responsible for creating an instance of an object on demand

View File

@ -5,7 +5,7 @@ using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Builder
namespace StyletIoC.Creation
{
/// <summary>
/// Delegate used to create an IRegistration

View File

@ -5,7 +5,7 @@ using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Builder
namespace StyletIoC.Creation
{
/// <summary>
/// Context used by IRegistration and ICreator to get things needed to create instances, etc

View File

@ -0,0 +1,29 @@
using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.Builders
{
internal class AbstractFactoryBinding : BuilderBindingBase
{
public AbstractFactoryBinding(Type serviceType)
: base(serviceType)
{
if (serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("Unbound generic type {0} can't be used as an abstract factory", serviceType.GetDescription()));
}
public override void Build(Container container)
{
var factoryType = container.GetFactoryForType(this.serviceType);
var creator = new AbstractFactoryCreator(factoryType);
var registration = new TransientRegistration(creator);
container.AddRegistration(new TypeKey(this.serviceType, this.Key), registration);
}
}
}

View File

@ -0,0 +1,59 @@
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.Builders
{
internal class BuilderBindTo : IBindTo
{
public Type ServiceType { get; private set; }
private BuilderBindingBase builderBinding;
public bool IsWeak { get { return this.builderBinding.IsWeak; } }
public string Key { get { return this.builderBinding.Key; } }
public BuilderBindTo(Type serviceType)
{
this.ServiceType = serviceType;
}
public IInScopeOrWithKeyOrAsWeakBinding ToSelf()
{
return this.To(this.ServiceType);
}
public IInScopeOrWithKeyOrAsWeakBinding To(Type implementationType)
{
this.builderBinding = new BuilderTypeBinding(this.ServiceType, implementationType);
return this.builderBinding;
}
public IInScopeOrWithKeyOrAsWeakBinding ToFactory<TImplementation>(Func<IRegistrationContext, TImplementation> factory)
{
this.builderBinding = new BuilderFactoryBinding<TImplementation>(this.ServiceType, factory);
return this.builderBinding;
}
public IWithKey ToAbstractFactory()
{
this.builderBinding = new AbstractFactoryBinding(this.ServiceType);
return this.builderBinding;
}
public IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(IEnumerable<Assembly> assemblies)
{
if (assemblies == null || !assemblies.Any())
assemblies = new[] { Assembly.GetCallingAssembly() };
this.builderBinding = new BuilderToAllImplementationsBinding(this.ServiceType, assemblies);
return this.builderBinding;
}
internal void Build(Container container)
{
this.builderBinding.Build(container);
}
}
}

View File

@ -0,0 +1,108 @@
using StyletIoC.Creation;
using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace StyletIoC.Internal.Builders
{
internal abstract class BuilderBindingBase : IInScopeOrWithKeyOrAsWeakBinding, IWithKey
{
protected Type serviceType;
protected RegistrationFactory registrationFactory;
public string Key { get; protected set; }
public bool IsWeak { get; protected set; }
public BuilderBindingBase(Type serviceType)
{
this.serviceType = serviceType;
// Default is transient
this.registrationFactory = (ctx, creator, key) => new TransientRegistration(creator);
}
IAsWeakBinding IInScopeOrAsWeakBinding.WithRegistrationFactory(RegistrationFactory registrationFactory)
{
if (registrationFactory == null)
throw new ArgumentNullException("registrationFactory");
this.registrationFactory = registrationFactory;
return this;
}
IInScopeOrAsWeakBinding IInScopeOrWithKeyOrAsWeakBinding.WithKey(string key)
{
this.Key = key;
return this;
}
protected void EnsureType(Type implementationType, Type serviceType = null)
{
serviceType = serviceType ?? this.serviceType;
if (!implementationType.IsClass || implementationType.IsAbstract)
throw new StyletIoCRegistrationException(String.Format("Type {0} is not a concrete class, and so can't be used to implemented service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
// Test this first, as it's a bit clearer than hitting 'type doesn't implement service'
if (implementationType.IsGenericTypeDefinition)
{
if (!serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("You can't use an unbound generic type to implement anything that isn't an unbound generic service. Service: {0}, Type: {1}", serviceType.GetDescription(), implementationType.GetDescription()));
// This restriction may change when I figure out how to pass down the correct type argument
if (serviceType.GetTypeInfo().GenericTypeParameters.Length != implementationType.GetTypeInfo().GenericTypeParameters.Length)
throw new StyletIoCRegistrationException(String.Format("If you're registering an unbound generic type to an unbound generic service, both service and type must have the same number of type parameters. Service: {0}, Type: {1}", serviceType.GetDescription(), implementationType.GetDescription()));
}
else if (serviceType.IsGenericTypeDefinition)
{
if (implementationType.GetGenericArguments().Length > 0)
throw new StyletIoCRegistrationException(String.Format("You cannot bind the bound generic type {0} to the unbound generic service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
else
throw new StyletIoCRegistrationException(String.Format("You cannot bind the non-generic type {0} to the unbound generic service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
}
if (!implementationType.Implements(this.serviceType))
throw new StyletIoCRegistrationException(String.Format("Type {0} does not implement service {1}", implementationType.GetDescription(), serviceType.GetDescription()));
}
// Convenience...
protected void BindImplementationToService(Container container, Type implementationType, Type serviceType = null)
{
serviceType = serviceType ?? this.serviceType;
if (serviceType.IsGenericTypeDefinition)
{
var unboundGeneric = new UnboundGeneric(implementationType, container, this.registrationFactory);
container.AddUnboundGeneric(new TypeKey(serviceType, this.Key), unboundGeneric);
}
else
{
var creator = new TypeCreator(implementationType, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(serviceType, this.Key ?? creator.AttributeKey), registration);
}
}
// Convenience...
protected IRegistration CreateRegistration(IRegistrationContext registrationContext, ICreator creator)
{
return this.registrationFactory(registrationContext, creator, this.Key);
}
void IWithKey.WithKey(string key)
{
this.Key = key;
}
void IAsWeakBinding.AsWeakBinding()
{
this.IsWeak = true;
}
public abstract void Build(Container container);
}
}

View File

@ -0,0 +1,32 @@
using StyletIoC.Creation;
using StyletIoC.Internal.Creators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.Builders
{
internal class BuilderFactoryBinding<TImplementation> : BuilderBindingBase
{
private Func<IRegistrationContext, TImplementation> factory;
public BuilderFactoryBinding(Type serviceType, Func<IRegistrationContext, TImplementation> factory)
: base(serviceType)
{
if (this.serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("A factory cannot be used to implement unbound generic type {0}", this.serviceType.GetDescription()));
this.EnsureType(typeof(TImplementation));
this.factory = factory;
}
public override void Build(Container container)
{
var creator = new FactoryCreator<TImplementation>(this.factory, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(this.serviceType, this.Key), registration);
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.Builders
{
internal class BuilderToAllImplementationsBinding : BuilderBindingBase
{
private IEnumerable<Assembly> assemblies;
public BuilderToAllImplementationsBinding(Type serviceType, IEnumerable<Assembly> assemblies)
: base(serviceType)
{
this.assemblies = assemblies;
}
public override void Build(Container container)
{
var candidates = from type in assemblies.Distinct().SelectMany(x => x.GetTypes())
let baseType = type.GetBaseTypesAndInterfaces().FirstOrDefault(x => x == this.serviceType || x.IsGenericType && x.GetGenericTypeDefinition() == this.serviceType)
where baseType != null
select new { Type = type, Base = baseType.ContainsGenericParameters ? baseType.GetGenericTypeDefinition() : baseType };
foreach (var candidate in candidates)
{
try
{
this.EnsureType(candidate.Type, candidate.Base);
this.BindImplementationToService(container, candidate.Type, candidate.Base);
}
catch (StyletIoCRegistrationException e)
{
Debug.WriteLine(String.Format("Unable to auto-bind type {0} to {1}: {2}", candidate.Base.Name, candidate.Type.GetDescription(), e.Message), "StyletIoC");
}
}
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StyletIoC.Internal.Builders
{
internal class BuilderTypeBinding : BuilderBindingBase
{
private Type implementationType;
public BuilderTypeBinding(Type serviceType, Type implementationType)
: base(serviceType)
{
this.EnsureType(implementationType);
this.implementationType = implementationType;
}
public override void Build(Container container)
{
this.BindImplementationToService(container, this.implementationType);
}
}
}

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using StyletIoC.Internal;
using StyletIoC.Internal.Registrations;
using StyletIoC.Internal.RegistrationCollections;
@ -380,14 +380,14 @@ namespace StyletIoC.Internal
// Couldn't find this type - is it a 'get all' collection type? (i.e. they've put IEnumerable<TypeWeCanResolve> in a ctor param)
IRegistration registration;
if (!this.TryRetrieveGetAllRegistration(typeKey, out registration))
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", typeKey.Type.Description()));
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", typeKey.Type.GetDescription()));
// Got this far? Good. There's actually a 'get all' collection type. Proceed with that
registrations = new SingleRegistration(registration);
}
else
{
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", typeKey.Type.Description()));
throw new StyletIoCRegistrationException(String.Format("No registrations found for service {0}.", typeKey.Type.GetDescription()));
}
}
@ -402,7 +402,7 @@ namespace StyletIoC.Internal
}
catch (StyletIoCRegistrationException e)
{
throw new StyletIoCRegistrationException(String.Format("{0} Service type: {1}, key: '{2}'", e.Message, typeKey.Type.Description(), typeKey.Key), e);
throw new StyletIoCRegistrationException(String.Format("{0} Service type: {1}, key: '{2}'", e.Message, typeKey.Type.GetDescription(), typeKey.Key), e);
}
}
@ -418,7 +418,7 @@ namespace StyletIoC.Internal
}
// Is there an existing registration for this type?
if (unboundGenerics.Any(x => x.Type == unboundGeneric.Type))
throw new StyletIoCRegistrationException(String.Format("Multiple registrations for type {0} found", typeKey.Type.Description()));
throw new StyletIoCRegistrationException(String.Format("Multiple registrations for type {0} found", typeKey.Type.GetDescription()));
unboundGenerics.Add(unboundGeneric);
}
@ -427,7 +427,7 @@ namespace StyletIoC.Internal
{
// Not thread-safe, as it's only called from the builder
if (!serviceType.IsInterface)
throw new StyletIoCCreateFactoryException(String.Format("Unable to create a factory implementing type {0}, as it isn't an interface", serviceType.Description()));
throw new StyletIoCCreateFactoryException(String.Format("Unable to create a factory implementing type {0}, as it isn't an interface", serviceType.GetDescription()));
if (this.factoryBuilder == null)
{
@ -522,7 +522,7 @@ namespace StyletIoC.Internal
}
catch (TypeLoadException e)
{
throw new StyletIoCCreateFactoryException(String.Format("Unable to create factory type for interface {0}. Ensure that the interface is public, or add [assembly: InternalsVisibleTo(StyletIoCContainer.FactoryAssemblyName)] to your AssemblyInfo.cs", serviceType.Description()), e);
throw new StyletIoCCreateFactoryException(String.Format("Unable to create factory type for interface {0}. Ensure that the interface is public, or add [assembly: InternalsVisibleTo(StyletIoCContainer.FactoryAssemblyName)] to your AssemblyInfo.cs", serviceType.GetDescription()), e);
}
return constructedType;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;
@ -47,7 +47,7 @@ namespace StyletIoC.Internal.Creators
var ctorsWithAttribute = this.Type.GetConstructors().Where(x => x.GetCustomAttributes(typeof(InjectAttribute), false).Any()).ToList();
if (ctorsWithAttribute.Count > 1)
{
throw new StyletIoCFindConstructorException(String.Format("Found more than one constructor with [Inject] on type {0}.", this.Type.Description()));
throw new StyletIoCFindConstructorException(String.Format("Found more than one constructor with [Inject] on type {0}.", this.Type.GetDescription()));
}
else if (ctorsWithAttribute.Count == 1)
{
@ -55,7 +55,7 @@ namespace StyletIoC.Internal.Creators
var key = ((InjectAttribute)ctorsWithAttribute[0].GetCustomAttribute(typeof(InjectAttribute), false)).Key;
var cantResolve = ctor.GetParameters().Where(p => !this.parentContext.CanResolve(p.ParameterType, key) && !p.HasDefaultValue).FirstOrDefault();
if (cantResolve != null)
throw new StyletIoCFindConstructorException(String.Format("Found a constructor with [Inject] on type {0}, but can't resolve parameter '{1}' (of type {2}, and doesn't have a default value).", this.Type.Description(), cantResolve.Name, cantResolve.ParameterType.Description()));
throw new StyletIoCFindConstructorException(String.Format("Found a constructor with [Inject] on type {0}, but can't resolve parameter '{1}' (of type {2}, and doesn't have a default value).", this.Type.GetDescription(), cantResolve.Name, cantResolve.ParameterType.GetDescription()));
}
else
{
@ -66,7 +66,7 @@ namespace StyletIoC.Internal.Creators
if (ctor == null)
{
throw new StyletIoCFindConstructorException(String.Format("Unable to find a constructor for type {0} which we can call.", this.Type.Description()));
throw new StyletIoCFindConstructorException(String.Format("Unable to find a constructor for type {0} which we can call.", this.Type.GetDescription()));
}
}
@ -84,7 +84,7 @@ namespace StyletIoC.Internal.Creators
}
catch (StyletIoCRegistrationException e)
{
throw new StyletIoCRegistrationException(String.Format("{0} Required by parameter '{1}' of type {2} (which is a {3}).", e.Message, x.Name, this.Type.Description(), x.ParameterType.Description()), e);
throw new StyletIoCRegistrationException(String.Format("{0} Required by parameter '{1}' of type {2} (which is a {3}).", e.Message, x.Name, this.Type.GetDescription(), x.ParameterType.GetDescription()), e);
}
}
// For some reason we need this cast...

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Diagnostics;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using StyletIoC.Internal.RegistrationCollections;
using System;
using System.Collections.Generic;
@ -30,7 +30,7 @@ namespace StyletIoC.Internal.RegistrationCollections
public IRegistrationCollection AddRegistration(IRegistration registration)
{
if (this.registration.Type == registration.Type)
throw new StyletIoCRegistrationException(String.Format("Multiple registrations for type {0} found.", registration.Type.Description()));
throw new StyletIoCRegistrationException(String.Format("Multiple registrations for type {0} found.", registration.Type.GetDescription()));
return new RegistrationCollection(new List<IRegistration>() { this.registration, registration });
}

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -62,7 +62,7 @@ namespace StyletIoC.Internal.Registrations
{
Debug.Assert(ctx == this.parentContext);
if (this.disposed)
throw new ObjectDisposedException(String.Format("ChildContainer registration for type {0}", this.Type.Description()));
throw new ObjectDisposedException(String.Format("ChildContainer registration for type {0}", this.Type.GetDescription()));
if (this.instance != null)
return this.instance;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;
@ -37,7 +37,7 @@ namespace StyletIoC.Internal.Registrations
public override Expression GetInstanceExpression(ParameterExpression registrationContext)
{
if (this.disposed)
throw new ObjectDisposedException(String.Format("Singleton registration for type {0}", this.Type.Description()));
throw new ObjectDisposedException(String.Format("Singleton registration for type {0}", this.Type.GetDescription()));
if (this.instanceExpression != null)
return this.instanceExpression;

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -5,13 +5,26 @@ using System.Reflection;
namespace StyletIoC.Internal
{
internal static class TypeExtensions
/// <summary>
/// Useful extension methods on Type
/// </summary>
public static class TypeExtensions
{
/// <summary>
/// Return all base types and interfaces implemented by the given type (and its ancestors)
/// </summary>
/// <param name="type">Type to return base types and interfaces for</param>
/// <returns>Base types and interfaces implemented by the given type</returns>
public static IEnumerable<Type> GetBaseTypesAndInterfaces(this Type type)
{
return type.GetInterfaces().Concat(type.GetBaseTypes());
}
/// <summary>
/// Return all base types implemented by the given type (and their base types, etc)
/// </summary>
/// <param name="type">Type to interrogate</param>
/// <returns>Base types implemented by the given type</returns>
public static IEnumerable<Type> GetBaseTypes(this Type type)
{
for (var baseType = type.BaseType; baseType != null; baseType = baseType.BaseType)
@ -20,6 +33,15 @@ namespace StyletIoC.Internal
}
}
/// <summary>
/// Determine if any of the type's base types or interfaces is equal to the given service type. Also checks generic types
/// </summary>
/// <remarks>
/// For example, given I1{T} and C1{T} : I1{T}, typeof(C1{int}).Implemements(typeof(I1{}) returns true.
/// </remarks>
/// <param name="implementationType">Implementation type</param>
/// <param name="serviceType">Service type</param>
/// <returns>Whether the implementation type implements the service type</returns>
public static bool Implements(this Type implementationType, Type serviceType)
{
return serviceType.IsAssignableFrom(implementationType) ||
@ -41,22 +63,35 @@ namespace StyletIoC.Internal
{ typeof(double), "double" },
{ typeof(decimal), "decimal" },
{ typeof(bool), "bool" },
{ typeof(string), "string" },
};
public static string Description(this Type type)
/// <summary>
/// Return a human-readable description of the given type
/// </summary>
/// <remarks>
/// This returns things like 'List{int}' instead of 'List`1[System.Int32]'
/// </remarks>
/// <param name="type">Type to generate the description for</param>
/// <returns>Description of the given type</returns>
public static string GetDescription(this Type type)
{
if (type.IsGenericTypeDefinition)
return String.Format("{0}<{1}>", type.Name.Split('`')[0], String.Join(", ", type.GetTypeInfo().GenericTypeParameters.Select(x => x.Name)));
var genericArguments = type.GetGenericArguments();
string name;
if (genericArguments.Length > 0)
{
return String.Format("{0}<{1}>", type.Name.Split('`')[0], String.Join(", ", genericArguments.Select(x =>
{
string name;
return primitiveNameMapping.TryGetValue(x, out name) ? name : x.Name;
})));
}
return type.Name;
else
{
return primitiveNameMapping.TryGetValue(type, out name) ? name : type.Name;
}
}
}
}

View File

@ -1,4 +1,4 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;

View File

@ -1,5 +1,6 @@
using StyletIoC.Builder;
using StyletIoC.Creation;
using StyletIoC.Internal;
using StyletIoC.Internal.Builders;
using StyletIoC.Internal.Creators;
using StyletIoC.Internal.Registrations;
using System;
@ -22,12 +23,6 @@ namespace StyletIoC
/// <returns></returns>
IInScopeOrWithKeyOrAsWeakBinding ToSelf();
/// <summary>
/// Bind the specified service to another type which implements that service. E.g. builder.Bind{IMyClass}().To{MyClass}(), and request an IMyClass: you'll get a MyClass.
/// </summary>
/// <typeparam name="TImplementation">Type to bind the service to</typeparam>
IInScopeOrWithKeyOrAsWeakBinding To<TImplementation>();
/// <summary>
/// Bind the specified service to another type which implements that service. E.g. builder.Bind{IMyClass}().To(typeof(MyClass)), and request an IMyClass: you'll get a MyClass.
/// </summary>
@ -50,7 +45,7 @@ namespace StyletIoC
/// Discover all implementations of the service in the specified assemblies / the current assembly, and bind those to the service
/// </summary>
/// <param name="assemblies">Assemblies to search. If empty / null, searches the current assembly</param>
IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(params Assembly[] assemblies);
IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(IEnumerable<Assembly> assemblies);
}
/// <summary>
@ -111,264 +106,6 @@ namespace StyletIoC
IInScopeOrAsWeakBinding WithKey(string key);
}
/// <summary>
/// Extension methods providing scopes other than transitnet
/// </summary>
public static class StyletIoCScopeExtensions
{
/// <summary>
/// Modify the scope of the binding to Singleton. One instance of this implementation will be generated for this binding.
/// </summary>
public static IAsWeakBinding InSingletonScope(this IInScopeOrAsWeakBinding builder)
{
return builder.WithRegistrationFactory((ctx, creator, key) => new SingletonRegistration(ctx, creator));
}
/// <summary>
/// Modify the scope binding to Per Container. One instance of this implementation will be generated per container / child container.
/// </summary>
public static IAsWeakBinding InPerContainerScope(this IInScopeOrAsWeakBinding builder)
{
return builder.WithRegistrationFactory((ctx, creator, key) => new PerContainerRegistration(ctx, creator, key));
}
}
internal class BuilderBindTo : IBindTo
{
public Type ServiceType { get; private set; }
private BuilderBindingBase builderBinding;
public bool IsWeak { get { return this.builderBinding.IsWeak; } }
public string Key { get { return this.builderBinding.Key; } }
public BuilderBindTo(Type serviceType)
{
this.ServiceType = serviceType;
}
public IInScopeOrWithKeyOrAsWeakBinding ToSelf()
{
return this.To(this.ServiceType);
}
public IInScopeOrWithKeyOrAsWeakBinding To<TImplementation>()
{
return this.To(typeof(TImplementation));
}
public IInScopeOrWithKeyOrAsWeakBinding To(Type implementationType)
{
this.builderBinding = new BuilderTypeBinding(this.ServiceType, implementationType);
return this.builderBinding;
}
public IInScopeOrWithKeyOrAsWeakBinding ToFactory<TImplementation>(Func<IRegistrationContext, TImplementation> factory)
{
this.builderBinding = new BuilderFactoryBinding<TImplementation>(this.ServiceType, factory);
return this.builderBinding;
}
public IWithKey ToAbstractFactory()
{
this.builderBinding = new AbstractFactoryBinding(this.ServiceType);
return this.builderBinding;
}
public IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(params Assembly[] assemblies)
{
if (assemblies == null || assemblies.Length == 0)
assemblies = new[] { Assembly.GetCallingAssembly() };
this.builderBinding = new BuilderToAllImplementationsBinding(this.ServiceType, assemblies);
return this.builderBinding;
}
internal void Build(Container container)
{
this.builderBinding.Build(container);
}
}
internal abstract class BuilderBindingBase : IInScopeOrWithKeyOrAsWeakBinding, IWithKey
{
protected Type serviceType;
protected RegistrationFactory registrationFactory;
public string Key { get; protected set; }
public bool IsWeak { get; protected set; }
public BuilderBindingBase(Type serviceType)
{
this.serviceType = serviceType;
// Default is transient
this.registrationFactory = (ctx, creator, key) => new TransientRegistration(creator);
}
IAsWeakBinding IInScopeOrAsWeakBinding.WithRegistrationFactory(RegistrationFactory registrationFactory)
{
if (registrationFactory == null)
throw new ArgumentNullException("registrationFactory");
this.registrationFactory = registrationFactory;
return this;
}
IInScopeOrAsWeakBinding IInScopeOrWithKeyOrAsWeakBinding.WithKey(string key)
{
this.Key = key;
return this;
}
protected void EnsureType(Type implementationType, Type serviceType = null)
{
serviceType = serviceType ?? this.serviceType;
if (!implementationType.IsClass || implementationType.IsAbstract)
throw new StyletIoCRegistrationException(String.Format("Type {0} is not a concrete class, and so can't be used to implemented service {1}", implementationType.Description(), serviceType.Description()));
// Test this first, as it's a bit clearer than hitting 'type doesn't implement service'
if (implementationType.IsGenericTypeDefinition)
{
if (!serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("You can't use an unbound generic type to implement anything that isn't an unbound generic service. Service: {0}, Type: {1}", serviceType.Description(), implementationType.Description()));
// This restriction may change when I figure out how to pass down the correct type argument
if (serviceType.GetTypeInfo().GenericTypeParameters.Length != implementationType.GetTypeInfo().GenericTypeParameters.Length)
throw new StyletIoCRegistrationException(String.Format("If you're registering an unbound generic type to an unbound generic service, both service and type must have the same number of type parameters. Service: {0}, Type: {1}", serviceType.Description(), implementationType.Description()));
}
else if (serviceType.IsGenericTypeDefinition)
{
if (implementationType.GetGenericArguments().Length > 0)
throw new StyletIoCRegistrationException(String.Format("You cannot bind the bound generic type {0} to the unbound generic service {1}", implementationType.Description(), serviceType.Description()));
else
throw new StyletIoCRegistrationException(String.Format("You cannot bind the non-generic type {0} to the unbound generic service {1}", implementationType.Description(), serviceType.Description()));
}
if (!implementationType.Implements(this.serviceType))
throw new StyletIoCRegistrationException(String.Format("Type {0} does not implement service {1}", implementationType.Description(), serviceType.Description()));
}
// Convenience...
protected void BindImplementationToService(Container container, Type implementationType, Type serviceType = null)
{
serviceType = serviceType ?? this.serviceType;
if (serviceType.IsGenericTypeDefinition)
{
var unboundGeneric = new UnboundGeneric(implementationType, container, this.registrationFactory);
container.AddUnboundGeneric(new TypeKey(serviceType, this.Key), unboundGeneric);
}
else
{
var creator = new TypeCreator(implementationType, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(serviceType, this.Key ?? creator.AttributeKey), registration);
}
}
// Convenience...
protected IRegistration CreateRegistration(IRegistrationContext registrationContext, ICreator creator)
{
return this.registrationFactory(registrationContext, creator, this.Key);
}
void IWithKey.WithKey(string key)
{
this.Key = key;
}
void IAsWeakBinding.AsWeakBinding()
{
this.IsWeak = true;
}
public abstract void Build(Container container);
}
internal class BuilderTypeBinding : BuilderBindingBase
{
private Type implementationType;
public BuilderTypeBinding(Type serviceType, Type implementationType) : base(serviceType)
{
this.EnsureType(implementationType);
this.implementationType = implementationType;
}
public override void Build(Container container)
{
this.BindImplementationToService(container, this.implementationType);
}
}
internal class BuilderFactoryBinding<TImplementation> : BuilderBindingBase
{
private Func<IRegistrationContext, TImplementation> factory;
public BuilderFactoryBinding(Type serviceType, Func<IRegistrationContext, TImplementation> factory) : base(serviceType)
{
if (this.serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("A factory cannot be used to implement unbound generic type {0}", this.serviceType.Description()));
this.EnsureType(typeof(TImplementation));
this.factory = factory;
}
public override void Build(Container container)
{
var creator = new FactoryCreator<TImplementation>(this.factory, container);
var registration = this.CreateRegistration(container, creator);
container.AddRegistration(new TypeKey(this.serviceType, this.Key), registration);
}
}
internal class BuilderToAllImplementationsBinding : BuilderBindingBase
{
private IEnumerable<Assembly> assemblies;
public BuilderToAllImplementationsBinding(Type serviceType, IEnumerable<Assembly> assemblies) : base(serviceType)
{
this.assemblies = assemblies;
}
public override void Build(Container container)
{
var candidates = from type in assemblies.Distinct().SelectMany(x => x.GetTypes())
let baseType = type.GetBaseTypesAndInterfaces().FirstOrDefault(x => x == this.serviceType || x.IsGenericType && x.GetGenericTypeDefinition() == this.serviceType)
where baseType != null
select new { Type = type, Base = baseType.ContainsGenericParameters ? baseType.GetGenericTypeDefinition() : baseType };
foreach (var candidate in candidates)
{
try
{
this.EnsureType(candidate.Type, candidate.Base);
this.BindImplementationToService(container, candidate.Type, candidate.Base);
}
catch (StyletIoCRegistrationException e)
{
Debug.WriteLine(String.Format("Unable to auto-bind type {0} to {1}: {2}", candidate.Base.Name, candidate.Type.Description(), e.Message), "StyletIoC");
}
}
}
}
internal class AbstractFactoryBinding : BuilderBindingBase
{
public AbstractFactoryBinding(Type serviceType) : base(serviceType)
{
if (serviceType.IsGenericTypeDefinition)
throw new StyletIoCRegistrationException(String.Format("Unbound generic type {0} can't be used as an abstract factory", serviceType.Description()));
}
public override void Build(Container container)
{
var factoryType = container.GetFactoryForType(this.serviceType);
var creator = new AbstractFactoryCreator(factoryType);
var registration = new TransientRegistration(creator);
container.AddRegistration(new TypeKey(this.serviceType, this.Key), registration);
}
}
/// <summary>
/// This IStyletIoCBuilder is the only way to create an IContainer. Binding are registered using the builder, than an IContainer generated.
/// </summary>
@ -380,12 +117,6 @@ namespace StyletIoC
/// <param name="serviceType">Service to bind</param>
IBindTo Bind(Type serviceType);
/// <summary>
/// Search the specified assembly(s) / the current assembly for concrete types, and self-bind them
/// </summary>
/// <param name="assemblies">Assembly(s) to search, or leave empty / null to search the current assembly</param>
void Autobind(params Assembly[] assemblies);
/// <summary>
/// Search the specified assembly(s) / the current assembly for concrete types, and self-bind them
/// </summary>
@ -448,18 +179,6 @@ namespace StyletIoC
}
}
/// <summary>
/// Search the specified assembly(s) / the current assembly for concrete types, and self-bind them
/// </summary>
/// <param name="assemblies">Assembly(s) to search, or leave empty / null to search the current assembly</param>
public void Autobind(params Assembly[] assemblies)
{
// Have to do null-or-empty check here as well, otherwise GetCallingAssembly returns this one....
if (assemblies == null || assemblies.Length == 0)
assemblies = new[] { Assembly.GetCallingAssembly() };
this.Autobind(assemblies.AsEnumerable());
}
/// <summary>
/// Once all bindings have been set, build an IContainer from which instances can be fetches
/// </summary>
@ -484,6 +203,19 @@ namespace StyletIoC
/// </summary>
public static class StyletIoCBuilderExtensions
{
/// <summary>
/// Search the specified assembly(s) / the current assembly for concrete types, and self-bind them
/// </summary>
/// <param name="builder">Builder to call on</param>
/// <param name="assemblies">Assembly(s) to search, or leave empty / null to search the current assembly</param>
public static void Autobind(this IStyletIoCBuilder builder, params Assembly[] assemblies)
{
// Have to do null-or-empty check here as well, otherwise GetCallingAssembly returns this one....
if (assemblies == null || assemblies.Length == 0)
assemblies = new[] { Assembly.GetCallingAssembly() };
builder.Autobind(assemblies.AsEnumerable());
}
/// <summary>
/// Bind the specified service (interface, abstract class, concrete class, unbound generic, etc) to something
/// </summary>
@ -492,5 +224,43 @@ namespace StyletIoC
{
return builder.Bind(typeof(TService));
}
/// <summary>
/// Bind the specified service to another type which implements that service. E.g. builder.Bind{IMyClass}().To{MyClass}(), and request an IMyClass: you'll get a MyClass.
/// </summary>
/// <typeparam name="TImplementation">Type to bind the service to</typeparam>
public static IInScopeOrWithKeyOrAsWeakBinding To<TImplementation>(this IBindTo bindTo)
{
return bindTo.To(typeof(TImplementation));
}
/// <summary>
/// Discover all implementations of the service in the specified assemblies / the current assembly, and bind those to the service
/// </summary>
/// <param name="bindTo">Binder to call on</param>
/// <param name="assemblies">Assemblies to search. If empty / null, searches the current assembly</param>
public static IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(this IBindTo bindTo, params Assembly[] assemblies)
{
// Have to do null-or-empty check here as well, otherwise GetCallingAssembly returns this one....
if (assemblies == null || assemblies.Length == 0)
assemblies = new[] { Assembly.GetCallingAssembly() };
return bindTo.ToAllImplementations(assemblies.AsEnumerable());
}
/// <summary>
/// Modify the scope of the binding to Singleton. One instance of this implementation will be generated for this binding.
/// </summary>
public static IAsWeakBinding InSingletonScope(this IInScopeOrAsWeakBinding builder)
{
return builder.WithRegistrationFactory((ctx, creator, key) => new SingletonRegistration(ctx, creator));
}
/// <summary>
/// Modify the scope binding to Per Container. One instance of this implementation will be generated per container / child container.
/// </summary>
public static IAsWeakBinding InPerContainerScope(this IInScopeOrAsWeakBinding builder)
{
return builder.WithRegistrationFactory((ctx, creator, key) => new PerContainerRegistration(ctx, creator, key));
}
}
}

View File

@ -1,6 +1,6 @@
using NUnit.Framework;
using StyletIoC;
using StyletIoC.Builder;
using StyletIoC.Creation;
using System;
using System.Collections.Generic;
using System.Linq;