Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ public class PreserveAttribute : Attribute
{
}

/// <summary>
/// Any method, property, attribute, or constructor annotated with this attribute
/// will be used to inject dependencies.
/// </summary>
/// <remarks>
/// It is an error to apply this attribute to more than one constructor. However,
/// it can be applied to any number of methods, properties, or fields.
/// </remarks>
/// <seealso cref="IObjectResolver.Inject"/>
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class InjectAttribute : PreserveAttribute
{
Expand Down
144 changes: 144 additions & 0 deletions VContainer/Assets/VContainer/Runtime/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,169 @@

namespace VContainer
{
/// <summary>
/// A container from which dependencies can be retrieved.
/// </summary>
/// <remarks>
/// <para>
/// This documentation uses "container" and "resolver" interchangeably.
/// </para>
/// <para>
/// When disposed of with <see cref="IDisposable.Dispose"/>, all registered
/// dependencies that implement <see cref="IDisposable"/> will be disposed
/// of except for registered instances and <see cref="Lifetime.Transient"/>
/// dependencies.
/// </para>
/// <para>
/// Types must generally be registered with the interfaces you'll use to
/// resolve them, but <see cref="IDisposable"/> is an exception to the rule;
/// if an object implements it, VContainer will detect it and clean it up regardless
/// of how it's resolved.
/// </para>
/// </remarks>
public interface IObjectResolver : IDisposable
{
/// <summary>
/// Retrieves the dependency mapped to the given <paramref name="type"/>.
/// </summary>
/// <remarks>
/// Most dependencies will be constructed the first time they're resolved.
/// The main exceptions are <see cref="Lifetime.Transient"/> dependencies
/// and registered instances.
/// </remarks>
/// <param name="type">
/// The type of the dependency to resolve. Note that multiple types can
/// map to the same dependency.
/// </param>
/// <exception cref="VContainerException">
/// No dependency is registered to <paramref name="type"/>.
/// </exception>
/// <returns>The dependency that is mapped to <paramref name="type"/>.</returns>
/// <seealso cref="IObjectResolverExtensions.Resolve{T}"/>
object Resolve(Type type);

/// <summary>
/// Resolves a dependency from the given <paramref name="registration"/>.
/// </summary>
/// <remarks>
/// This method is mostly used internally. Application code should prefer using
/// <see cref="IObjectResolverExtensions.Resolve{T}"/> instead.
/// </remarks>
/// <param name="registration">
/// The registration used to resolve the requested dependency. Doesn't technically
/// need to be a part of this <see cref="IObjectResolver"/>.
/// </param>
/// <returns>The dependency that <paramref name="registration"/> resolved.</returns>
/// <seealso cref="IObjectResolverExtensions.Resolve{T}"/>
/// <seealso cref="Resolve(System.Type)"/>
object Resolve(IRegistration registration);

/// <summary>
/// Creates a child <see cref="IScopedObjectResolver"/> that can resolve this
/// container's dependencies or register its own.
/// </summary>
/// <param name="installation">
/// A delegate used to configure the newly-created container. Defaults to
/// <see langword="null"/>, which does not register any new dependencies.
/// </param>
/// <returns>
/// A new container that will use this one to resolve any dependencies that
/// it hasn't registered itself.
/// </returns>
/// <seealso cref="Lifetime"/>
IScopedObjectResolver CreateScope(Action<IContainerBuilder> installation = null);

/// <summary>
/// Provides an existing object with the dependencies it needs to function.
/// </summary>
/// <remarks>
/// <para>
/// This method will call all methods annotated with <see cref="InjectAttribute"/>.
/// It will also populate all properties and fields annotated with <see cref="InjectAttribute"/>.
/// </para>
/// <para>
/// Although it is unlikely that you will need to inject the same object
/// multiple times, doing so will work as expected even if using a different
/// container as long as the registrations are valid; all <see cref="InjectAttribute"/>-annotated
/// methods will be called and properties/fields populated the same way
/// they were the first time.
/// </para>
/// </remarks>
/// <param name="instance">
/// The object that will be injected with its requested dependencies.
/// </param>
/// <exception cref="VContainerException">
/// A dependency that <paramref name="instance"/> requires couldn't be resolved.
/// </exception>
/// <seealso cref="InjectAttribute"/>
/// <seealso cref="Unity.ObjectResolverUnityExtensions"/>
void Inject(object instance);
}

/// <summary>
/// A container from which dependencies can be retrieved.
/// </summary>
/// <remarks>
/// "container" and "resolver" are often used interchangeably.
/// </remarks>
public interface IScopedObjectResolver : IObjectResolver
{
IObjectResolver Root { get; }
IScopedObjectResolver Parent { get; }
bool TryGetRegistration(Type type, out IRegistration registration);
}

/// <summary>
/// Describes the rules that determine when a dependency is constructed.
/// </summary>
public enum Lifetime
{
/// <summary>
/// <c>Transient</c> dependencies are instantiated each time they're resolved.
/// </summary>
/// <remarks>
/// <para>
/// We don't recommend registering an <see cref="IDisposable"/>-implementing
/// dependency as <c>Transient</c>. VContainer will <b>not</b> dispose of
/// disposable <c>Transient</c> dependencies. Although you can do so manually
/// in your own code, refactoring the dependency's <see cref="Lifetime"/>
/// may lead to non-obvious bugs.
/// </para>
/// </remarks>
Transient,

/// <summary>
/// <c>Singleton</c> dependencies are instantiated exactly once within a
/// tree of <see cref="IObjectResolver"/>s.
/// </summary>
/// <remarks>
/// <para>
/// It is an error to register more than one <c>Singleton</c> dependency
/// with the same type. Doing so will throw a <see cref="VContainerException"/>.
/// </para>
/// <para>
/// When an <see cref="IObjectResolver"/> is disposed of, <c>Singleton</c>
/// dependencies that are <see cref="IDisposable"/> will be disposed of
/// as well, <i>except</i> for registered instances.
/// </para>
/// </remarks>
/// <seealso cref="ContainerBuilderExtensions.RegisterInstance{T}"/>
Singleton,

/// <summary>
/// <c>Scoped</c> dependencies are instantiated once, but can be overridden
/// in child scopes.
/// </summary>
/// <remarks>
/// <para>
/// When an <see cref="IObjectResolver"/> is disposed of, <c>Scoped</c>
/// dependencies that are <see cref="IDisposable"/> will be disposed of as well.
/// </para>
/// <para>
/// If your game only has one <see cref="Unity.LifetimeScope"/>, then this
/// is equivalent to <see cref="Singleton"/>.
/// </para>
/// </remarks>
Scoped
}

Expand Down
89 changes: 89 additions & 0 deletions VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,100 @@

namespace VContainer
{
/// <summary>
/// Used to configure the dependencies that will be provided by an <see cref="IObjectResolver"/>
/// at runtime.
/// </summary>
/// <remarks>
/// <para>
/// This interface will generally be used at the start of your game and at major
/// transition points (e.g. when loading new scenes).
/// </para>
/// <para>
/// Instances of this interface will not affect containers after they're built.
/// Do not pass it into <see cref="RegisterBuildCallback">build callbacks</see>,
/// you can't modify containers that way.
/// </para>
/// </remarks>
/// <seealso cref="ContainerBuilderExtensions"/>
/// <seealso cref="IObjectResolver"/>
public interface IContainerBuilder
{
/// <summary>
/// The object that represents the application.
/// </summary>
/// <remarks>
/// Will generally be an instance or subclass of <see cref="Unity.LifetimeScope"/>,
/// but it can be anything that represents the application.
/// </remarks>
object ApplicationOrigin { get; set; }

/// <summary>
/// Adds a registration to this container builder.
/// </summary>
/// <remarks>
/// You likely won't use this method directly; instead you'll want to use
/// one of the extension methods defined in <see cref="ContainerBuilderExtensions"/>.
/// </remarks>
/// <param name="registrationBuilder">
/// The registration builder that will be used to construct the registrations
/// that resolve dependencies.
/// </param>
/// <typeparam name="T">
/// The type of the registration builder.
/// </typeparam>
/// <returns>
/// The provided <paramref name="registrationBuilder"/>, for fluently
/// chaining further method calls.
/// </returns>
/// <seealso cref="ContainerBuilderExtensions"/>
T Register<T>(T registrationBuilder) where T : RegistrationBuilder;

/// <summary>
/// Enqueues a <see langword="delegate"/> to be executed once the container
/// is built.
/// </summary>
/// <remarks>
/// <para>
/// Possible use cases include (but are not limited to):
/// <list type="bullet">
/// <item>
/// Forcibly resolving dependencies even if they're not explicitly used.
/// Try not to do that too often.
/// </item>
/// <item>
/// Setting external configuration such as global event handlers.
/// </item>
/// <item>
/// Signalling that your application is ready to proceed (e.g. that a
/// level has finished loading).
/// </item>
/// </list>
/// </para>
/// <para>
/// Callbacks will be executed in the order they're registered.
/// </para>
/// </remarks>
/// <param name="container">
/// The callback to execute. The container it provides can be used to resolve dependencies.
/// </param>
void RegisterBuildCallback(Action<IObjectResolver> container);

/// <summary>
/// Returns <see langword="true"/> if this builder will register a
/// dependency with the given <paramref name="type"/>.
/// </summary>
/// <param name="type">
/// The <see cref="Type"/> of the dependency whose registration we're looking for.
/// </param>
/// <param name="includeInterfaceTypes">
/// If <see langword="true"/>, looks for interfaces and base classes included
/// in <see cref="IRegistration.InterfaceTypes"/> as well. Otherwise
/// looks only for the registration's implementation type. Defaults to <see langword="false"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if a dependency is registered to <paramref name="type"/>.
/// </returns>
bool Exists(Type type, bool includeInterfaceTypes = false);
}

Expand Down
Loading