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

Хотите знать, можно ли достичь следующего:

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

1 ответ

Решение

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

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

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

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

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

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

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

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

или же

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);
    }
}
Другие вопросы по тегам