Autofac: разрешение типов вариантов с аргументами типов in и out - PullRequest
9 голосов
/ 06 сентября 2011

Этот вопрос является продолжением моего предыдущего вопроса: Autofac: скрытие нескольких контравариантных реализаций за одним составным .

Я пытаюсь найти границы того, что мы можем сделать с помощью ковариации и контравариантности Autofac. Я заметил, что ContravariantRegistrationSource в Autofac поддерживает только универсальные интерфейсы с одним универсальным параметром, отмеченным ключевым словом in. Кажется, это ограничивает полезность этой функции, и мне интересно, есть ли у Autofac другие способы расширения поддержки ковариации и контравариантности.

Должен признать, что я не спрашиваю об этом из-за реального дизайна приложения, над которым я работаю. Я сознательно пытаюсь найти пределы Autofac ради образования.

Итак, рассмотрим следующий интерфейс:

public interface IConverter<in TIn, out TOut>
{
    TOut Convert(TIn value);
}

И следующая реализация:

public class ObjectToStringConverter : IConverter<object, string>
{
    string IConverter<object, string>.Convert(object value)
    {
        return value.ToString();
    }
}

И следующая регистрация:

var builder = new ContainerBuilder();

builder.RegisterSource(new ContravariantRegistrationSource());

builder.RegisterType<ObjectToStringConverter>()
    .As<IConverter<object, string>>();

var container = builder.Build();

С этим дизайном и конфигурацией, я бы ожидал, что смогу сделать это:

// This call succeeds because IConverter<object, string> is
// explicitly registered.
container.Resolve<IConverter<object, string>>();

// This call fails, although IConverter<string, object> is
// assignable from IConverter<object, string>.
container.Resolve<IConverter<string, object>>();

Или позвольте мне выразить это более абстрактно, с учетом следующих определений:

public class A { }
public class B : A { }
public class C : B { }

public class AToCConverter : IConverter<A, C> { ... }

и следующая регистрация:

builder.RegisterType<AToCConverter>()
    .As<IConverter<C, A>>();

Я ожидаю, что следующие вызовы будут успешными:

container.Resolve<IConverter<C, A>>();
container.Resolve<IConverter<B, B>>();
container.Resolve<IConverter<A, C>>();

Как мы можем сделать это с Autofac?

Ответы [ 2 ]

4 голосов
/ 08 сентября 2011

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

Мы можем сделать контравариантное «разрешение», потому что, учитывая аргумент общего типа, мы можем найти всетипы базы / интерфейса, которым этот аргумент может быть назначен.То есть, учитывая string, мы можем искать реализации для object, IComparable и т. Д.

Переход в противоположном направлении - от типа аргумента ко всем его подклассам - не так прост.Учитывая object, нам нужен какой-то способ искать все остальное.

Может быть возможно использовать знания о конкретных компонентах, зарегистрированных в контейнере, например, сканировать все компоненты в поисках возможных реализаций и работать в обратном направлении,но это не очень хорошо для Autofac, потому что мы полагаемся на модель «тянуть», чтобы лениво создавать компоненты во многих случаях.

Надеюсь, это пища для размышлений, интересно посмотреть, что вы придумали.

1 голос
/ 07 сентября 2011

Вы правы, заметив, что ContravariantRegistrationSource распознает типы только с одним общим параметром.Глядя на источник (в настоящее время около 166 строки), вы увидите это ограничение прямо здесь.Глядя на то, как источник регистрации требуется для предоставления возможных кандидатов, я могу понять, что снятие ограничения потребует более сложной реализации.

Я бы сказал, что это не доказывает, что вы достигли пределов Autofac, только пределы этого конкретного источника регистрации.Я оставлю это в качестве упражнения для читателя, чтобы улучшить реализацию ContravariantRegistrationSource, и я уверен, что проект Autofac более чем рад принять его обратно в ядро.

...