Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Attributes and Delegates

Namespace: SimplEnteiner.Core.Attributes, SimplEnteiner.Core Source: Core/Attributes/, Core/Delegates.cs

InjectAttribute

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Method)]
public sealed class InjectAttribute : Attribute
{
}

The marker attribute the container looks for (via Constants.InjectAttributeType = typeof(InjectAttribute)) to identify:

  • Which constructor to use, when a class has more than one public constructor. TypeAnalyzes.GetInjectableConstructor looks for exactly one constructor marked [Inject]; if none is marked, it falls back to the public constructor with the most parameters; if more than one constructor is marked [Inject], an Exception is thrown ("Multiple constructors with InjectAttribute attribute in {type}").
  • Which fields/properties/methods to inject, via TypeAnalyzes.GetInjectableMembers, which scans public and non-public instance fields, properties, and methods for the [Inject] attribute (AllowMultiple = false is the default since it's not specified, but inherit: true is used when checking IsDefined).
public class OrderService
{
    [Inject] private readonly ILogger _logger; // field injection

    [Inject] public IClock Clock { get; set; } // property injection

    public OrderService() { }               // ignored (no [Inject], but a marked ctor exists below)

    [Inject]
    public OrderService(IRepository repository) // used, because it's marked
    {
        Repository = repository;
    }

    public IRepository Repository { get; }

    [Inject]
    public void Configure(IOptions options) // method injection — invoked once, with resolved arguments
    {
        // ...
    }
}

Note: because InjectAttribute targets Constructor too, it can also be applied directly to a single-constructor class without effect (there's nothing to disambiguate), but it's harmless.

IdAttribute

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]
public class IdAttribute : Attribute
{
    public IdAttribute(object id);
    public object Id { get; }
}

Applied to a constructor parameter, field, or property to request a specific conditional/id-based binding (registered via .WithId(id)) instead of the default binding for that type. Read by Resolver via GetCustomAttribute<IdAttribute>() on the relevant ParameterInfo/FieldInfo/PropertyInfo.

container.Bind<IStorage>().To<LocalStorage>().AsSingle().WithId("local").Apply();
container.Bind<IStorage>().To<CloudStorage>().AsSingle().WithId("cloud").Apply();

public class SyncJob
{
    public SyncJob([Id("local")] IStorage source, [Id("cloud")] IStorage destination)
    {
        Source = source;
        Destination = destination;
    }

    public IStorage Source { get; }
    public IStorage Destination { get; }
}

Resolution for an [Id]-attributed member/parameter temporarily overrides ResolutionContext.Id for the duration of that single dependency's resolution (see Resolver.ResolveMember/ResolveConstructorWithArguments), then restores the previous value — so nested dependencies are unaffected unless they too specify their own [Id].

You can also resolve an id-based binding directly, without any attribute, via:

IStorage local = container.Resolve<IStorage>("local");

ResolverFunc Delegate

Source: Core/Delegates.cs

namespace SimplEnteiner.Core
{
    public delegate object ResolverFunc(Type interfaceType, Scope scope, object id = null);
}

A public delegate type matching the signature of IResolver.Resolve(Type, Scope, object). It exists as a reusable delegate shape for consumers who want to pass around "a resolve operation" as a first-class value (e.g., in custom IResolver/IScopeFactory implementations), though the built-in Resolver class does not currently expose itself as a ResolverFunc instance directly.

CircularDependencyException

Source: TypeAnalyzes.cs (nested type)

public sealed class CircularDependencyException : Exception
{
    public CircularDependencyException(IEnumerable<Type> circularPath);
    public IReadOnlyList<Type> CircularPath { get; }
}

Thrown by TypeAnalyzes.CanResolveAllDependencies when HasCyclicDependencies detects a cycle in a type's dependency graph. The message includes the full cycle path (A -> B -> C -> A) for diagnostics, and CircularPath exposes the same list programmatically. See Error Handling and Logging for the full exception catalogue.

Continue to Convention-Based Binding.