使用Simple Injector进行自我绑定

时间:2014-01-16 15:34:39

标签: c# console-application simple-injector constructor-injection

我正在尝试使用Simple Injector学习依赖注入,所以我创建了一个简单的程序。我查看了大量的例子,但没有找到类似的东西。 Ninject有一个类似的例子,它们使用

将实现自我绑定到自身
Bind<Samurai>().ToSelf();

但我没有在简单的注射器中找到任何自我约束的例子。目前该程序运行良好,我得到了我正在寻找的预期结果,但这是正确的方法吗?该程序是最后一个代码示例。

界面

public interface ICar
{
    void Move(string direction, int distance);
    void Stop();
}

public class Driver
{
    private readonly ICar _car = null;

    public Driver(ICar car)
    {
        this._car = car;
    }

    public void Drive(string direction, int distance)
    {
        _car.Move(direction, distance);
    }

    public void Brake()
    {
        _car.Stop();
    }
}

实施

public class Ferrari : ICar
{
    public void Move(string direction, int distance)
    {
        Console.WriteLine("I am driving {0} really fast for {1} miles", direction, distance);
    }
    public void Stop()
    {
        Console.WriteLine("I am slamming on the brakes because I'm going too fast");
    }
}

public class Lexus : ICar
{
    public void Move(string direction, int distance)
    {
        Console.WriteLine("I am driving {0} for {1} miles", direction, distance);
    }
    public void Stop()
    {
        Console.WriteLine("I am applying the brakes");
    }
}

计划

class Program
{
    static void Main(string[] args)
    {
        var container = new Container();
        container.Options.AllowOverridingRegistrations = true;

        container.Register<ICar, Lexus>();
        container.Register<ICar, Ferrari>();

        container.Verify();

        var johnDoeLexus = new Driver(container.GetInstance<Lexus>());

        johnDoeLexus.Drive("North", 10);
        johnDoeLexus.Brake();

        var johnDoeFerrari = new Driver(container.GetInstance<Ferrari>());

        johnDoeFerrari.Drive("North", 10);
        johnDoeFerrari.Brake();

        Console.ReadLine();
    }
}

3 个答案:

答案 0 :(得分:11)

在Simple Injector中进行自我绑定的方法是使用以下注册:

container.Register<Samurai>();

这是一个捷径:

container.Register<Samurai, Samurai>();

这又是一个捷径:

container.Register<Samurai, Samurai>(Lifestyle.Transient);

致电Register<TService, TImplementation>时。你基本上要求容器是每次请求TImplementation时创建TService

然而,简单注入器可以在没有显式注册的情况下解析具体类型(例如您的Samurai),但在大多数情况下,最好显式注册所有类型(或至少是您直接从容器请求的所有类型)打电话给GetInstance<Something>())。通过注册该类型,您可以让Simple Injector知道您想解决它,这允许Simple Injector验证它是否可以被解析(当调用Verify()时)并允许Simple Injector到diagnostic analysis时类型。

请注意,虽然致电GetInstance<TService>会请求注册TService。在您的代码中,您正在执行此操作:

container.Register<ICar, Lexus>();
var johnDoeLexus = new Driver(container.GetInstance<Lexus>());

换句话说,您正在注册ICar,但您没有解析ICar,而是正在解析Lexus。由于Lexus尚未注册为TService,因此Simple Injector对该类型一无所知,只会为此Lexus创建一个新注册。这与执行此操作相同:

container.Register<ICar, Lexus>();
container.Register<Lexus, Lexus>();
var johnDoeLexus = new Driver(container.GetInstance<Lexus>());

这可能会有问题,因为您可能希望ICar拥有不同的生活方式:

container.Register<ICar, Lexus>(Lifestyle.Singleton);

var instance1 = container.GetInstance<Lexus>();
var instance2 = container.GetInstance<Lexus>();

您可能希望对GetInstance的两次调用都返回相同的(单个)实例,但事实并非如此,因为Simple Injector将为Lexus创建一个新的(瞬态)注册,并且每次调用Lexus时都会创建一个新的GetInstance

另请注意您使用AllowOverridingRegistrations。使用AllowOverridingRegistrations大部分时间都是个坏主意。虽然其他容器允许您通过多次调用Register来注册一组相同抽象类型,但Simple Injector不允许这样做。相反,Simple Injector包含RegisterCollection方法。例如,注册多辆汽车可以按如下方式进行:

container.RegisterCollection<ICar>(new[] { typeof(Lexus), typeof(Ferrari) });

您可以按如下方式申请这些车辆:

var cars = container.GetAllInstances<ICar>();

或者您可以将一组汽车注入一个类型的构造函数:

public SomeType(IEnumerable<ICar> cars) {
    this.cars = cars;
}

如果您使用RegisterCollection注册了汽车列表,则使用GetInstance<ICar>请求一辆汽车将失败,因为Simple Injector不知道应该返回哪辆汽车。

API以designed explicitly这种方式,使开发人员不太可能在注册中出现仍然编译甚至运行的错误,但结果不正确。使用AllowOverridingRegistrations标记容器可以替换已进行的注册。这在容器的配置被拆分为多个库并由多个应用程序重用的情况下非常有用。它会覆盖已进行的注册。这意味着您将放弃此之前的注册。大多数时候你实际上并不想要这个。

答案 1 :(得分:1)

自我绑定是Simple Injector的隐式行为。因此,像

这样的一行
Bind<Samurai>().ToSelf();
使用Simple Injector根本不需要

答案 2 :(得分:1)

我认为这个问题更多 Ninject的目的是什么 .ToSelf()

Ninject使用.ToSelf()作为

的更易阅读的版本
kernel.Bind<Samurai>().To<Samurai>();

.ToSelf()不那么多余:

kernel.Bind<Samurai>().ToSelf();

您要执行.ToSelf()的原因是,您可以继续链接方法,例如定义范围。 Ninjects具有隐式自我绑定,因此您不需要.ToSelf(),除非您要定义范围或其他内容:

kernel.Bind<Samurai>().ToSelf().InSingletonScope();

kernel.Bind<Samurai>().ToSelf().InThreadScope()

Simple Injector的api不同,正如Robert Petemeier所说,.ToSelf()不是必需的:

container.Register<Samurai>(Lifestyle.Singleton);

关于依赖注入:

当您要求Lexus时,您要求ICar

ICar car = container.GetInstance<ICar>()

根据您注册Lexus的具体课程,决定是否需要FerrariICar

Simple Injector is well documented