Разрешить общий интерфейс с помощью простого инжектора

Интересно, можно ли добиться следующего:

container.GetInstance<IWordFacade<,,,>>();

Пока мне не удалось. Вот несколько примеров кода:

IWordFacade<T1,T2,T3,T4>{
    T1 DoSomething(T2);
}

public class ConcreteFacade1 : IWordFacade<int,long,double,decimal>{
    int DoSomething(long param){
        //....
    }
}

public class ConcreteFacade2 : IWordFacade<short,string,float,char>{
    short DoSomething(string param){
        // ...
    }
}

... учитывая эти типы/классы, я пытаюсь найти что-то, что позволит мне вернуть ConcreteFacade1 или 2 в зависимости от того, как настроен контейнер для разрешения интерфейса (вот где у меня возникают проблемы).

Я пытался:

container.Register(typeof(IWordFacade<,,,>), typeof(ConcreteFacade1));

в результате появляется сообщение об ошибке: IWordFacade‹,,,> не является зарегистрированным открытым универсальным типом.


person bbqchickenrobot    schedule 17.03.2014    source источник
comment
И какой тип вы ожидаете, что контейнер вернется для вас? Можете ли вы дать немного больше контекста?   -  person Steven    schedule 17.03.2014
comment
Я хотел бы использовать любой из типов ConcreteFacade в зависимости от того, как настроен контейнер... это зависит от определенной конфигурации app.config и определяется во время выполнения.   -  person bbqchickenrobot    schedule 17.03.2014


Ответы (1)


Предоставленный вами код не является допустимым C# и не будет компилироваться. Вы не можете указать открытый общий тип внутри тегов ‹ и >. Вы должны указать закрытый общий тип. Однако допустимо следующее С#:

container.GetInstance(typeof(IWordFacade<,,,>));

Хотя это скомпилируется, это все равно не будет работать в Simple Injector (или любом другом DI-фреймворке), поскольку фреймворки всегда хотят вернуть вам конкретный экземпляр, но вы не можете создать экземпляр для открытого универсального типа, что совершенно очевидно. , так как открытый универсальный тип — это просто шаблон. Вам необходимо заполнить пустые поля, чтобы иметь возможность создать экземпляр. Другими словами, вам нужен закрытый универсальный тип.

Но как контейнер должен знать, какой закрытый универсальный экземпляр он должен создать с учетом этого открытого универсального типа? Существует бесконечное количество возможных перестановок.

Хотя вы можете зарегистрировать открытый универсальный тип следующим образом:

container.Register(typeof(IWordFacade<,,,>), typeof(WordImpl<,,,>));

Вам нужно будет запросить закрытый универсальный интерфейс для вашего контейнера DI, чтобы иметь возможность разрешить его:

container.GetInstance<IWordFacade<Foo, Bar, FooBar>>();

or

container.GetInstance(typeof(IWordFacade<Foo, Bar, FooBar>));

ОБНОВЛЕНИЕ

Из вашего обновления я понимаю, что вы либо регистрируете ConcreteFacade1, либо ConcreteFacade2, исходя из файла конфигурации, но никогда оба. Чтобы сделать это (в C#), вам нужен не универсальный интерфейс, иначе вы сможете программировать только с System.Object, что не очень полезно (и безопасно для типов). Итак, пусть ваш универсальный IWordFacade<T1,T2,T3,T4> наследуется от следующего неуниверсального интерфейса:

interface IWordFacade {
    object DoSomething(object value);
}

Таким образом, вы можете сделать следующую регистрацию:

contianer.Register<IWordFacade, ConcreteFacade1>();

И вы можете решить:

container.GetInstance<IWordFacade>();

Или, конечно, внедрить IWordFacade в конструктор другого типа.

Теперь вам нужно внедрить два метода в реализации. Если у вас есть несколько реализаций, полезно определить базовый класс. Этот базовый класс может реализовать необобщенный метод интерфейса:

public abstract class WordFacadeBase<T1, T2, T3, T4> : IWordFacade<T1, T2, T3, T4> {
    public abstract T1 DoSomething(T2 value);
    object IWordFacade.DoSomething(object value) {
        return DoSomething(T2)value);
    }
}
person Steven    schedule 17.03.2014
comment
Спасибо за помощь, Стивен - и извините за опечатку в начале регистрации (набирал быстро и из памяти). В любом случае... Я действительно хочу избежать необходимости предоставлять общие типы, когда я довольно часто вызываю container.GetInstance(). Итак, что я сделал, так это использовал некоторые методы .net для достижения того, что я искал. - person bbqchickenrobot; 17.03.2014
comment
Привет, Стив, спасибо за обновление - это именно то, что я пытался сделать и что я в итоге реализовал, поскольку другого способа добиться этого не было. К сожалению, мне приходится вызывать метод GetInstance() более чем в нескольких местах (надстройка над словом), и я не хочу, чтобы обслуживание превратилось в кошмар. Спасибо за понимание и за то, что написали это - сэкономил мне время :) - person bbqchickenrobot; 17.03.2014
comment
Это правильный способ использования в приложении ASP.NET. GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(MyApp.ILogger)). Спасибо. - person Dilhan Jayathilake; 01.02.2017
comment
@Dilhan, на этот вопрос невозможно ответить без надлежащего контекста. Возможно, вы захотите попробовать опубликовать новый вопрос здесь, на SO. - person Steven; 01.02.2017