Внедрение параметров общего типа с помощью AutoFac - PullRequest
3 голосов
/ 18 января 2011

Я думаю, что я действительно смущен тем, что я могу сделать с 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";
    }
}

Создайте универсальный класс PersonHandler для работы с разными типами людей и хотели бы, чтобы PersonHandler инстанцировал человека в соответствующее время, поэтому я не хочу, чтобы экземпляр 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>, поэтому это приводит к "I Base"

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

Это дает ошибку, говорящую, что PersonHandler<FakePerson> не является открытым типом

Так что теперь я гоняюсь за своим рассказом весь деньи, честно говоря, это становится утомительным,

ПОЖАЛУЙСТА, ПОМОГИТЕ

Ответы [ 2 ]

10 голосов
/ 19 января 2011

(почти) правильное решение - это то, которое вы уже пробовали:

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>.

0 голосов
/ 18 января 2011

При разрешении службы, зарегистрированной с открытым универсальным типом (например, PersonHandler<>), необходимо указать точный закрытый тип, который вы хотите обработать, и Autofac сгенерирует его для вас.

В этом случае вы позвонили container.Resolve<PersonHandler<PersonBase>>(), поэтому вам вернули PersonHandler<PersonBase>. Теперь, если вы посмотрите на свой класс PersonHandler и замените PersonBase на T, результирующий метод DoWork будет выглядеть следующим образом:

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

Таким образом, вызов SaySomething() использует метод базового класса.

Я не совсем уверен, что именно вы пытаетесь сделать, но похоже, что вы хотите решить, какую конкретную реализацию PersonBase использовать при регистрации. Если это так, вы можете вместо этого использовать функцию Delegate Factory в Autofac:

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, который вы действительно хотите использовать в программе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...