Конструкторы без параметров
Возможно то, что вы хотите, за исключением одного: вы не можете использовать конструкторы не по умолчанию с аргументами универсального типа . Вы не можете избежать этого.
Отчасти проблема заключается в том, что вы не можете принудительно применять сигнатуры конкретных методов конструктора из интерфейса, поэтому нет никакого способа гарантировать, что вся реализация IUnitOfWork
будетимеют один и тот же конструктор.
Самое простое решение - это отказаться от использования конструкторов и вместо этого использовать инициализацию объекта:
public interface IUnitOfWork
{
Foo Foo { get; set; }
}
private BaseResult<TUnitOfWork> GetUnitOfWorkByCompiledModel<TUnitOfWork>(DbCompiledModel compiledModel) where TUnitOfWork : IUnitOfWork, new()
{
return new BaseResult<TUnitOfWork>
{
Payload = new TUnitOfWork()
{
Foo = myFoo
};
};
}
Я думаю, что это соответствует вашим ожиданиям, будучи минимальным изменением.
Разрешение интерфейсов
Но если я передам Тип интерфейса в качестве параметра, как создать экземпляр DbUnitOfWork или CustomUnitOfWork. например,
GetUnitOfWorkByCompiledModel<IUnitOfWork>(compiledModel);
GetUnitOfWorkByCompiledModel<ICustomUnitOfWork>(compiledModel);
Если вы намереваетесь использовать типы интерфейсов без конкретных типов, то приведенный выше ответ является неполным.
Независимо от тогоЧто касается проблемы универсального типа, если вы хотите преобразовать интерфейс в конкретный тип, вам необходимо зарегистрировать, какой конкретный тип вы хотите использовать.
Чаще всего это делается с помощью инфраструктуры внедрения зависимостей. Эти рамки просят вас зарегистрировать все необходимые типы, например, пример .NET Core:
services.AddTransient<IUnitOfWork, MyUnitOfWork>();
services.AddTransient<ICustomUnitOfWork, MyCustomUnitOfWork>();
И среда затем будет использовать эту регистрацию для автоматического заполнения параметров конструктора для вас:
public class Example
{
public Example(ICustomUnitOfWork uow)
{
}
}
Подход с хорошей практикой требует, чтобы вы внедрили эту инъекцию зависимостей через всю вашу инфраструктуру, поэтому вам никогда не нужно явно вызывать какой-либо конструктор (и вместо этого сделайте это для инфраструктуры DI).
Возможноиспользуйте сервисный локатор , который по сути является структурой DI, которую вы вызываете по желанию. Небольшой пример использования:
private BaseResult<TUnitOfWork> GetUnitOfWorkByCompiledModel<TUnitOfWork>(DbCompiledModel compiledModel) where TUnitOfWork : IUnitOfWork, new()
{
var uow = myServiceLocator.Get<TUnitOfWork>();
uow.Foo = myFoo;
return new BaseResult<TUnitOfWork>
{
Payload = uow;
};
}
Это создает экземпляр того, какой конкретный тип был зарегистрирован в интерфейсе, который вы используете как универсальный параметр.
Однако сервисные локаторы, как правило, считаются антишаблоном , и я настоятельно рекомендую вам избегать более чистого подхода IoC, чем этот.
Я не могу подробно остановиться на внедрении зависимостей в рамкаходин ответ StackOverflow. Я предлагаю вам посмотреть внедрение зависимостей, поскольку оно делает именно то, что вы ожидаете (получение конкретного типа с помощью ссылки на интерфейс)