使用AutoFac注入通用类型参数

时间:2011-01-18 16:01:51

标签: generics dependency-injection autofac

我觉得我对使用AutoFac可以做些什么感到困惑,有人可以让我走上正轨。

我有一个基本类型

class PersonBase{ 
    public string SaySomething(){
       return "I am base";
    }
}

我推导出两个具体的类

class FakePerson : PersonBase{
    public override string SaySomething(){
       return "I'm so Fake";
    }
}

class RealPerson : PersonBase{
    public override string SaySomething(){
       return "I am For Real";
    }
}

创建一个泛型类PersonH​​andler,以处理不同类型的人,并希望PersonH​​andler在适当的时候实例化该人,所以我不想要一个Person实例注入,只需要派生类型

class PersonHandler<T>
    where T : PersonBase, new() {

    T _Person;    

    public DoWork(){
        _Person = new T();
        _Person.SaySomething();
    }
}

现在我尝试使用处理程序,在注册下面详细说明的类型后,会有不同的结果。

var ph = contrainer.Resolve<PersonHandler<PersonBase>>();
ph.DoWork();

我试图按以下方式注册类型

1. vBuilder.RegisterType<PersonHandler<FakePerson>>().As<PersonHandler<PersonBase>>();

这给了我一个错误,指出PersonHandler<FakePerson>无法分配给PersonHandler<PersonBase>(或者相反,我不会重新考虑哪个)

2. vBuilder.RegisterGeneric<typeof(PersonHandler<>)>
   vBuilder.RegisterType<FakePerson>().As<PersonBase>();

这不会将PersonBase解析为FakePerson,而只会提供PersonHandler<PersonBase>,因此会产生“我是基础”

3. vBuilder.RegisterGeneric(typeof(PersonHandler<FakePerson>)).As(typeof(PersonHandler<PersonBase>));

这给出了一个错误,指出PersonHandler<FakePerson>不是开放式

所以现在我一整天都在追逐我的故事,坦率地说,它正在逐渐退出乏味,

请帮助

2 个答案:

答案 0 :(得分:10)

(几乎)正确的解决方案就是你已经尝试过的那个:

builder.RegisterType<PersonHandler<FakePerson>>()
    .As<PersonHandler<PersonBase>>();

Autofac给你一个错误的原因是C#中的泛型类不能以这种方式工作。

也就是说,你不能写:

PersonHandler<PersonBase> ph = new PersonHandler<FakePerson>();

(在IDE中试一试 - 编译器会拒绝它。)

这样做的原因是C#4中添加的必需功能的逆转仅支持接口类型。

简而言之,如果您创建IPersonHandler<T>并将其用作服务,则上述代码将按预期工作:

interface IPersonHandler<in T>
    where T : PersonBase, new() {

    void DoWork();
}

请注意接口声明中的in参数。

PersonHandler<T>实施IPersonHandler<T>

class PersonHandler<T> : IPersonHandler<T>
    where T : PersonBase, new() {

然后像这样注册:

builder.RegisterType<PersonHandler<FakePerson>>()
    .As<IPersonHandler<PersonBase>>();

然后您可以使用以下方式获取处理程序:

IPersonHandler<PersonBase> handler =
    container.Resolve<IPersonHandler<PersonBase>>();

返回的handler对象的类型为PersonHandler<FakePerson>

答案 1 :(得分:0)

当您解析使用开放泛型类型(例如PersonHandler<>)注册的服务时,您需要指定要处理的确切关闭类型,Autofac将为您生成一个。

在这种情况下,您致电container.Resolve<PersonHandler<PersonBase>>(),因此您返回了PersonHandler<PersonBase>。现在,如果您查看PersonHandler课程,并将PersonBase替换为T,则生成的DoWork方法如下所示:

public DoWork(){
    _Person = new PersonBase();
    _Person.SaySomething();
}

因此SaySomething()调用使用基类上的方法。

我不确定您要做什么,但看起来您想要决定在注册时使用哪个具体的PersonBase实现。如果是这种情况,您可以尝试使用Autofac的Delegate Factory功能:

class PersonHandler
{
    private readonly Func<PersonBase> createPerson;

    public PersonHandler(Func<PersonBase> createPerson)
    {
        this.createPerson = createPerson;
    }

    public void DoWork()
    {
        PersonBase pb = this.createPerson();
        Console.WriteLine(pb.SaySomething());
    }
}

然后您可以按如下方式注册课程:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<RealPerson>().As<PersonBase>();
builder.RegisterType<PersonHandler>();
var context = builder.Build();
var handler = context.Resolve<PersonHandler>();
handler.DoWork();

通过更改此代码的第二行,您可以注册您实际想要在程序中使用的PersonBase类型。