C# Unity:告诉容器在注册类型时始终使用接口的特定实现

C# Unity:告诉容器在注册类型时始终使用接口的特定实现,c#,inversion-of-control,unity-container,C#,Inversion Of Control,Unity Container,我一直遇到这种情况:假设我有接口IFoo和几个实现,比如RedFoo:IFoo和BlackFoo:IFoo。然后,我在构造函数中使用IFoo的类: class BlackFooUser { public BlackFooUser(IFoo foo, other_parameters_here) {...} } class RedFooUser { public RedFooUser(IFoo foo, other_parameters_here) {...} } public c

我一直遇到这种情况:假设我有接口
IFoo
和几个实现,比如
RedFoo:IFoo
BlackFoo:IFoo
。然后,我在构造函数中使用
IFoo
的类:

class BlackFooUser
{
   public BlackFooUser(IFoo foo, other_parameters_here) {...}
}

class RedFooUser
{
   public RedFooUser(IFoo foo, other_parameters_here) {...}
}
public class BlackFooUser
{
    public BlackFooUser([Dependency(BlackFoo.UNITY_KEY)] IFoo foo)
    {
    }
}
如何告诉容器像往常一样解析所有其他参数,但在构造
blackfoooser
时始终使用
BlackFoo
,在构造
redfoooser
时始终使用
RedFoo
?我知道我可以在调用
Resolve()
时使用
ParameterOverride
,但我希望在解析
Red/blackfooouser
时使用相同的参数,因此这应该进入
RegisterType
RegisterFactory

我可以做类似的事情

container.RegisterFactory<RedFooUser>(c=>new RedFooUser(c.Resolve<RedFoo>(), other_parameters_here));
container.RegisterFactory(c=>newRedFooUser(c.Resolve(),此处为其他参数));
但这是相当冗长的,每次我添加新参数时都必须更改

有更好的办法吗

更新我特别希望通过容器注册做到这一点,而不修改涉及的类。原因有很多,但归结起来就是:

  • 在类中编码依赖信息违反了关注点分离
  • 在有许多类和许多依赖项的情况下,它通常伸缩性很差
  • 它限制了类的组合方式
  • 它将我限制为拥有源代码的类,并且可以随意修改

使用抽象类和泛型强制特定类型

Unity将自动解析一个具体类型,因此您不需要注册这些类型

public abstract class FooUser<TFoo> where TFoo : IFoo
{
    private readonly TFoo _foo;
    public FooUser(TFoo foo, other parameters)
    {
        _foo = foo;
    }
}

public class BlackFooUser : FooUser<BlackFoo>
{
    public BlackFooUser (BlackFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}

public class RedFooUser : FooUser<RedFoo>
{
    public RedFooUser (RedFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}
公共抽象类fooouser其中TFoo:IFoo
{
私有只读TFOOfoo;
公共FooUser(TFoo foo,其他参数)
{
_foo=foo;
}
}
公共类BlackFooUser:FooUser
{
公共BlackFooUser(BlackFoo-foo,其他参数)
:base(foo,其他参数)
{
}
}
公共类RedFooUser:FooUser
{
公共redfoooser(RedFoo-foo,其他参数)
:base(foo,其他参数)
{
}
}
完整复制如下-输出为:

使用foo'RedFoo'和其他参数'I'm the other'构造RedFooUser 使用foo'BlackFoo'和其他参数'I'm the other'构造BlackFooUser

void Main()
{
var container=newunitycontainer()
.RegisterInstance(“我是另一个参数”);
var foo1=container.Resolve();
var foo2=container.Resolve();
}
//定义其他方法、类和
公共接口IFoo
{
}
公共类BlackFoo:IFoo{}
公共类RedFoo:IFoo{}
公共抽象类FooUser,其中TFoo:IFoo
{
私有只读TFOOfoo;
公共FooUser(TFoo foo,字符串其他参数)
{
_foo=foo;
WriteLine($“用foo'{foo.GetType().Name}'和otherParameter'{otherParameter}'构造了{GetType().Name}”);
}
}
公共类BlackFooUser:FooUser
{
公共BlackFooUser(BlackFoo-foo,字符串其他参数)
:base(foo,其他参数)
{
}
}
公共类RedFooUser:FooUser
{
公共RedFooUser(RedFoo-foo,字符串其他参数)
:base(foo,其他参数)
{
}
}

使用抽象类和泛型强制特定类型

Unity将自动解析一个具体类型,因此您不需要注册这些类型

public abstract class FooUser<TFoo> where TFoo : IFoo
{
    private readonly TFoo _foo;
    public FooUser(TFoo foo, other parameters)
    {
        _foo = foo;
    }
}

public class BlackFooUser : FooUser<BlackFoo>
{
    public BlackFooUser (BlackFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}

public class RedFooUser : FooUser<RedFoo>
{
    public RedFooUser (RedFoo foo, other parameters)
        : base(foo, other parameters)
    {
    }
}
公共抽象类fooouser其中TFoo:IFoo
{
私有只读TFOOfoo;
公共FooUser(TFoo foo,其他参数)
{
_foo=foo;
}
}
公共类BlackFooUser:FooUser
{
公共BlackFooUser(BlackFoo-foo,其他参数)
:base(foo,其他参数)
{
}
}
公共类RedFooUser:FooUser
{
公共redfoooser(RedFoo-foo,其他参数)
:base(foo,其他参数)
{
}
}
完整复制如下-输出为:

使用foo'RedFoo'和其他参数'I'm the other'构造RedFooUser 使用foo'BlackFoo'和其他参数'I'm the other'构造BlackFooUser

void Main()
{
var container=newunitycontainer()
.RegisterInstance(“我是另一个参数”);
var foo1=container.Resolve();
var foo2=container.Resolve();
}
//定义其他方法、类和
公共接口IFoo
{
}
公共类BlackFoo:IFoo{}
公共类RedFoo:IFoo{}
公共抽象类FooUser,其中TFoo:IFoo
{
私有只读TFOOfoo;
公共FooUser(TFoo foo,字符串其他参数)
{
_foo=foo;
WriteLine($“用foo'{foo.GetType().Name}'和otherParameter'{otherParameter}'构造了{GetType().Name}”);
}
}
公共类BlackFooUser:FooUser
{
公共BlackFooUser(BlackFoo-foo,字符串其他参数)
:base(foo,其他参数)
{
}
}
公共类RedFooUser:FooUser
{
公共RedFooUser(RedFoo-foo,字符串其他参数)
:base(foo,其他参数)
{
}
}

注册类型时,可以给它们一个名称,然后在注入这些类型的构造函数中引用该名称。大概是这样的:

public class BlackFoo : IFoo
{
    public const string UNITY_KEY = "BlackFoo";
}
在您的容器注册中:

_container.RegisterType<IFoo, BlackFoo>(BlackFoo.UNITY_KEY, new ContainerControlledLifetimeManager());

这确保了当容器进行依赖项注入时,它将使用命名的
IFoo
注册。

当您注册类型时,您可以给它们一个名称,然后在注入这些类型的构造函数中引用该名称。大概是这样的:

public class BlackFoo : IFoo
{
    public const string UNITY_KEY = "BlackFoo";
}
在您的容器注册中:

_container.RegisterType<IFoo, BlackFoo>(BlackFoo.UNITY_KEY, new ContainerControlledLifetimeManager());
这确保了当容器进行依赖项注入时,它将使用命名的
IFoo
注册。

我提交给UnityContainer repo,Eugene Sadovoy的响应将我推向了这个答案

为了避免无限循环,可以注册默认fa