Stiletto is a .NET port of Dagger, the lightweight Android dependency injector from Square.

Stiletto provides declarative, configure-where-needed dependency injection that runs everywhere .NET does, including platforms lacking support for Reflection.Emit like MonoTouch. While some containers such as TinyIOC and Funq offer the same portability, they require large and brittle configuration methods to set up and use; Stiletto configuraton resides in modules that can be declared where they are used, and that can be easily reasoned about.

Basic Injection

Stiletto both constructs your objects and satisfies their dependencies. It relies on custom attributes on constructors and properties to determine what the dependencies are.

The [Inject] attribute on a constructor indicates that this constructor will be used to create that object, and its parameters are dependencies to be injected.

public class Jukebox
{
    private readonly IRepository<Album> albumRepository;

    [Inject]
    public Jukebox(IRepository<Album> albumRepository)
    {
        this.albumRepository = albumRepository;
    }

    ...
}

[Inject] can also be applied to properties that have both getters and setters:

public class Jukebox
{
    [Inject]
    public IRepository<Album> AlbumRepository { get; set; }

    ...
}

Objects are obtained from Stiletto by using the Container class:

var container = Container.Create(typeof(MusicModule));
var jukebox = container.Get<Jukebox>();
jukebox.PlayOne();

Stiletto can automatically satisfy any dependency that has an [Inject] attribute on it. While this is good, it is not sufficient: some objects require configuration, some are provided by third parties, etc. For these cases, dependencies can be provided by methods in so-called modules:

[Module]
public class MusicModule
{
    [Provides]
    public IRepository<Album> ProvideAlbumRepository(IWebService service)
    {
        return new WebServiceRepository<Album>(service, credentials);
    }
}

A module is any class that derives from Object has a [Module] attribute. Any public methods in a module that have a [Provides] attribute will be used by Stiletto to satisfy dependencies that couldn't otherwise be injected.

Singletons

Some objects naturally represent a one-of-a-kind entity that lives as long as your application does. For these objects, it's a mistake to create more than one. Stiletto supports such objects with the [Singleton] attribute. It can be applied both to classes that are singletons, and to provider methods as well:

[Singleton]
public class AppConfiguration
{
    private readonly ISettings settings;

    [Inject]
    public AppConfiguration(ISettings settings)
    {
        this.settings = settings;
    }
}
[Provides, Singleton]
public ISettings ProvideSettings()
{
    return new FileBasedSettings("./settings.ini");
}

Lazy Values

Some objects are just expensive to build up, and aren't always needed right away. If they are created eagerly, they might slow down application startup, detracting from the user experience and the perceived quality of your product.

For these objects, Stiletto supports injecting System.Lazy<T> instances. All that is needed is to specify a dependency as such, and Stiletto knows how to do the rest:

public class Application
{
    [Inject]
    Lazy<ISettings> Settings { get; set; }

    ...
}

Provider Injection

Unlike Guice, Stiletto does not support injecting its container directly. Nevertheless, sometimes you need multiple instances of the same dependency. To that end, dependencies can be declared of the type Stiletto.IProvider<T>, which will return a new instance of the provided type every time its Get method is invoked:

public class ProviderExample
{
    [Inject]
    public ProviderExample(IProvider<Object> objectProvider)
    {
        // objectProvider.Get() will return a new instance each time.
        Debug.Assert(objectProvider.Get() != objectProvider.Get())
    }
}

Note

When injecting Lazy or IProvider members, it is not necessary to explicitly provide Lazy or IProvider objects in modules; Stiletto will wrap the values itself. It is, in fact, an error to provide Lazy or IProvider members:

// NOT ALLOWED:
[Provides]
Lazy<Object> ProvideLazyObject()
{
    return new Lazy<Object>(() => new Object());
}

Stiletto employs an optional compile-time process that both statially analyzes the network of dependencies and modules, and generates CIL code to make the injections maximally efficent.

The compile-time process is implemented as a Fody weaver. Fody is a direct dependency

While the compiler plugin is technically optional in general, due to limitations of the Mono platform on iOS it is required for MonoTouch apps that make use of Lazy and IProvider injections. Additionally, a large part of Stiletto's value comes from the quick feedback cycle enabled by compile-time validation of your dependency graph. Not to mention the fact that, while reflection is powerful it is also very slow - using the compile-time plugin has been observed to provide a 100x reduction in time spent inside of Stiletto.

Validation

The dependencies and modules in your app can be thought of as a directed, acyclic graph. The types that you explicitly get from the container can be thought of as "entry points" into the graph. Starting from these entry points, Stiletto can analyze the graph and determine several useful things at compile-time. First, it can validate that there are no unsatisfied dependencies; it is no longer necessary to wait until runtime to discover that some critical dependency has been left out. Secondly, it can keep dead code out of your modules by detecting unused dependencies.

Code Generation

After your application's dependencies have been validated, Stiletto may modify your assemblies to make injection more efficient. After processing, you may find classes in your assembly with names like "YourOwnClass_CompiledBinding" or "$CompiledPlugin$"; these compiler-generated classes help eliminate the need for reflection, but should be considered as implementation details and not directly relied upon.

To start using Stiletto, all that is required is to get the library from NuGet.

To start using the compiler plugin, install the Stiletto.Fody package in your app's root project. If you are using Visual Studio, this is all that is required. For those using MonoDevelop or Xamarin Studio, setup of the compiler product is a little more compilicated. First, Fody must be installed and configured by itself; see the Fody setup for Mono for those details. Once xbuild and Fody are configured, get the Stiletto.Fody assembly and drop it in. Add a <Stiletto /> tag to your project's FodyWeavers.xml file, and build.

Contributions are welcome, whether in the form of pull requests, bug reports, or questions. Join the discussion at the GitHub project.

Stiletto is made available under the Apache 2.0 license; any contributions must be under the same license as well.

Copyright 2013 Benjamin Bader

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Stiletto's TeamCity CI hosting is generously provided by CodeBetter and JetBrains.

keyboard-centric bug tracker continuous integration server