4

我正在尝试使用 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();
    }
}
4

3 回答 3

11

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

container.Register<Samurai>();

这是一条捷径:

container.Register<Samurai, Samurai>();

这又是一条捷径:

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

打电话时Register<TService, TImplementation>。您基本上要求容器是在TImplementation每次TService请求 a 时创建。

然而,Simple Injector 可以在不显式注册的情况下解析具体类型(例如 your Samurai),但在大多数情况下,最好显式注册所有类型(或者至少是您通过调用直接从容器请求的所有类型GetInstance<Something>())。通过注册该类型,您可以让 Simple Injector 知道您想要解析它,这允许 Simple Injector 验证它是否可以被解析(当调用 时Verify()),它允许 Simple Injector对该类型进行诊断分析。

请注意,尽管调用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是一个坏主意。虽然其他容器允许您通过多次调用来注册一堆相同抽象的类型,但RegisterSimple 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,请求单个汽车 usingGetInstance<ICar>将失败,因为 Simple Injector 不知道它应该返回哪些汽车。

API 以这种方式明确设计,以降低开发人员在注册时出错的可能性,这些错误仍然可以编译甚至运行,但结果不正确。用AllowOverridingRegistrations允许替换已注册的容器标记容器。这在容器的配置被拆分为多个库并被多个应用程序重用的情况下很有用。它会覆盖已完成的注册。这意味着您将失去之前的注册。大多数时候你实际上并不想要这个。

于 2014-01-16T16:07:02.897 回答
1

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

Bind<Samurai>().ToSelf();

Simple Injector 根本不需要。

于 2014-01-16T15:50:52.960 回答
1

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

Ninject.ToSelf()用作​​更易读的版本

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

.ToSelf()更少冗余:

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

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

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

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

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

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

关于依赖注入:

您要求 aLexus什么时候应该要求 a ICar

ICar car = container.GetInstance<ICar>()

您决定是否需要LexusFerrari根据您注册ICar的具体课程。

Simple Injector 有据可查

于 2014-01-16T16:03:19.723 回答