C# IoC、依赖项注入和构造函数参数

C# IoC、依赖项注入和构造函数参数,c#,.net,dependency-injection,inversion-of-control,C#,.net,Dependency Injection,Inversion Of Control,我有一个服务,我希望能够根据控制反转原理创建,所以我创建了一个接口和一个服务类 public interface IMyService { void DoSomeThing1(); void DoSomeThing2(); void DoSomeThing3(); string GetSomething(); } public class MyService : IMyService { int _initialValue; //...

我有一个服务,我希望能够根据控制反转原理创建,所以我创建了一个接口和一个服务类

public interface IMyService
{
    void DoSomeThing1();
    void DoSomeThing2();
    void DoSomeThing3();
    string GetSomething();

}

public class MyService : IMyService
{
    int _initialValue;
    //...

    public MyService(int initialValue)
    {
        _initialValue = initialValue;
    }

    public void DoSomeThing1()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing2()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing3()
    {
        //Do something with _initialValue
        //...
    }

    public string GetSomething()
    {
        //Get something with _initialValue
        //...
    }
}
举个例子,我可以建立我的国际奥委会

public static class MyServiceIoc
{
    public static readonly IUnityContainer Container;

    static ServiceIoc()
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IMyService, MyService>();
        Container = container;
    }
}
公共静态类MyServiceIoc
{
公共静态只读IUnityContainer容器;
静态服务IOC()
{
IUnityContainer容器=新的UnityContainer();
container.RegisterType();
容器=容器;
}
}
问题在于构造函数参数。我可以用一个参数覆盖,比如

var service = MyServiceIoc.Container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});
var service=MyServiceIoc.Container.Resolve(新参数覆盖)
{                                                                                   
{“初始值”,42}
});
但我不想使用类型错误的参数。如果有人更改构造函数参数名称或添加一个参数,该怎么办?他不会被警告在复杂的时间,也许没有人会发现它,但最终用户。也许程序员更改了测试的IoC设置,但忘记了“发布”使用,那么即使是代码覆盖率为100%的代码库也不会检测到运行时错误

可以将Init函数添加到接口和服务中,但服务的用户必须理解这一点,并记住每次获得服务实例时都要调用Init函数。该服务变得不那么自我解释,并且对不正确的使用开放。如果方法不依赖于它们被调用的顺序,那么这是最好的

让它更安全的一个方法是在国际奥委会上设置一个创建功能

public static class MyServiceIoc
{
    //...
    public IMyService CreateService(int initialValue)
    {
        var service = Container.Resolve<IMyService>();
        service.Init(initialValue);

    }
}
公共静态类MyServiceIoc
{
//...
公共IMyService CreateService(int initialValue)
{
var service=Container.Resolve();
service.Init(initialValue);
}
}
但是,如果只查看服务及其接口,上面提到的问题仍然适用


有没有人有解决这个问题的可靠方案?如何在仍然使用IoC的情况下安全地将初始值传递给我的服务?

DI容器是基于反射的,基本上是弱类型的。这个问题比我们的问题要广泛得多——它无处不在

一旦执行以下操作,您就已经失去了编译时安全性:

IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
var service = container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});
在这里,如果其他人在您移开视线时更改了API,您将得到一个编译器错误。那很好


另一方面,当您传入一个基本依赖项并将其无形地转换为一个基本依赖项时,您会使客户机更难理解发生了什么

我建议改为这样设计:

public class MyService : IMyService
{
    AnotherClass _anotherObject;
    // ...

    public MyService(AnotherClass anotherObject)
    {
        _anotherObject = anotherObject;
    }

    // ...
}
这仍然很容易使用纯DI编写,并且类型安全:

IMyService service = new MyService(new AnotherClass(42));
在使用IoC的情况下,如何安全地将初始值传递给我的服务

在Unity中注册类型时,可以使用以下方法显式调用类型的构造函数:

container.RegisterInstance(新MyService(42));
这将为您提供您提到的编译时安全性,但代价是它将只实例化一次,并且将立即创建(与第一次请求时相反)


您也许可以通过使用一个方法重载来解决这个缺点,它接受LifetimeManager类。

这取决于您的用例,但在IoC容器世界中,它可能看起来像这样:

public class MyService : IMyService
{
    int _initialValue;
    // ...

    public MyService(IConfigurationService configurationService)
    {
        _initialValue = configurationService.GetInitialValueForMyService();
    }
    
    // ...
}
container.Register<IMyService, AdaptedMyService>();
如果具有构造函数参数的类在代码之外(例如,在第三方库中),则可以使用适配器

public class AdaptedMyService : MyService
{
    public AdaptedMyService(IConfigurationService configurationService)
        : base(configurationService.GetInitialValueForMyService())
    {
    }
}
然后在IoC容器中注册适配类,如下所示:

public class MyService : IMyService
{
    int _initialValue;
    // ...

    public MyService(IConfigurationService configurationService)
    {
        _initialValue = configurationService.GetInitialValueForMyService();
    }
    
    // ...
}
container.Register<IMyService, AdaptedMyService>();
container.Register();

谢谢!您关于另一个类的陈述可能是正确的,但这不是我关心的问题,这只是一个示例,因此我将其从示例中删除。这样就不会误会了。很抱歉给您带来不便!谢谢,但是我必须能够创建具有不同初始值的多个实例。