Создать экземпляр класса из интерфейса, который он реализует - PullRequest
0 голосов
/ 25 октября 2019

Привет, я пытаюсь создать универсальную UoWFactory, которая создает UnitOfWork (будет одна единица работы по умолчанию и несколько пользовательских реализаций). До сих пор я смог создать фабричный метод, который создает UoW по умолчанию и возвращает его. Я изменил следующий метод для возврата указанного UoW в зависимости от переданного параметра.

Текущая реализация

private BaseResult<IUnitOfWork> GetUnitOfWorkByCompiledModel<IUnitOfWork>(DbCompiledModel compiledModel)
{
    return new BaseResult<IUnitOfWork>
    {
        Payload = new DbUnitOfWork(_context, _dbRepository, _mapper, _entityMapper)
    };
}

Я хочу иметь что-то вроде этого

private BaseResult<TUoW> GetUnitOfWorkByCompiledModel<IUnitOfWork>(DbCompiledModel compiledModel) where TUoW :Class
{
    return new BaseResult<TUoW>
    {
        //Create instance of type TUoW where TUoW can be IUnitOfWork, ICustomUnitOfWork etc
        //DbUnitOfWork implements IUnitOfWork and CustomUnitOfWork implements ICustomUnitOfWork
        //All the TUoW will have constructors with identical parmeters
    };
}

Создатьэкземпляр класса является прямым

Activator.CreateInstance (Type type, object[] args);

Но если я передам Тип интерфейса в качестве параметра, как создать экземпляр DbUnitOfWork или CustomUnitOfWork. например: -

GetUnitOfWorkByCompiledModel<IUnitOfWork>(compiledModel);
GetUnitOfWorkByCompiledModel<ICustomUnitOfWork>(compiledModel);

Ответы [ 2 ]

1 голос
/ 25 октября 2019

Конструкторы без параметров

Возможно то, что вы хотите, за исключением одного: вы не можете использовать конструкторы не по умолчанию с аргументами универсального типа . Вы не можете избежать этого.

Отчасти проблема заключается в том, что вы не можете принудительно применять сигнатуры конкретных методов конструктора из интерфейса, поэтому нет никакого способа гарантировать, что вся реализация 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. Я предлагаю вам посмотреть внедрение зависимостей, поскольку оно делает именно то, что вы ожидаете (получение конкретного типа с помощью ссылки на интерфейс)

0 голосов
/ 25 октября 2019

Это будет работать, если класс DbUnitOfWork имеет правильное имя значения

Что вы хотите изменить

Payload = new DbUnitOfWork(_context, _dbRepository, _mapper, _entityMapper);

Изменить на

Payload = new DbUnitOfWork() {
    context = _context, 
    dbRepoitory = _dbRepository,
    mapper = _mapper,
    entityMapper = _entityMapper
};

надеюсь, что эта работа.

...