Neadle

Neadle is a simple dependency injection container for .NET.

Usage

Just like any DI container, you register dependencies before you resolve them. Additionally you can test whether dependencies can be resolved at any time.

Registration

There are three ways to register a dependency:

// Instance: register a ready-made single instance to be returned whenever an Animal is requested
Di.Register<Animal>(new Dog("Fido"));

// Factory: register a factory instead of a ready-made instance
Di.Register<Animal>(() => new Cat("Felix"));

// Type: register a concrete type to be constructed on demand
Di.Register<Animal, Raven>();
For these last two methods you can also specify an InstanceScope, with one of the following values:

Resolution

You can resolve a single dependency directly from the container:

// Who knows what type of animal lurks inside the container...
Animal beast = Di.Resolve<Animal>();

// No need to throw exceptions if no person was registered
if (Di.TryResolve<Person>(out Person someone))
{
    ...
}
Or create an instance of a class using constructor injection:
var instance = Di.CreateWithInjection<ClassWithParameterHeavyConstructor>();
If your class has multiple constructors, decorate one with the InjectAttribute:
public class MyClass
{
    public MyClass() 
    { 
    }
	
    [Inject]
    public MyClass(ISomeDependency dependency)
    {
    }
}

Lifetime scopes

To prevent dependencies from "living" inside the container indefinitely, you can create a lifetime scope and resolve dependencies from it. IDisposable dependencies are then disposed when the scope is disposed, however these dependencies must be registered:

A example:
Di.Register<Dog, Dog>(InstanceScope.SingleInstance);
Di.Register<Cat, Cat>(InstanceScope.SingleInstancePerLifetimeScope);
using (var scope = Di.BeginLifetimeScope())
{
	scope.Register<Horse, Horse>(new Horse(), false);
	scope.Register<Cow, Cow>(new Cow(), true);
	var animals = scope.Resolve(new Type[] { typeof(Dog), typeof(Cat), typeof(Cow) });
}

// If all types are IDisposable, Cat and Cow are now disposed. 

Contexts

In addition to scopes, which act as a form of isolation, it is also possible to specify a context when registering or resolving dependencies. Dependencies from a matching context are favoured over the default (empty) context:

Di.Register("Hello ", "First");
Di.Register("World", "Second");
Di.Register("!");

// Writes "Hello World!"; where no match could be made for the "Third" context, it 
// falls back to the context-less exclamation mark
Console.WriteLine(Di.Resolve<string>("First") + Di.Resolve<string>("Second") + Di.Resolve<string>("Third"));
Note that scopes and contexts are completely separate concepts, even though a scope can be created with a default context.