Подтвердить что ты не робот

Простой резольвер

Как вы создаете простой Resendver Dependency Resolver без использования каких-либо встроенных или библиотек, таких как Autofac, Ninject и т.д.

Это был мой вопрос для интервью.

Я написал этот простой код, и они сказали, что он выглядит не очень хорошо. Его как очень жестко закодированная идея.

public interface IRepository { }
interface IDataProvider
{
    List<string> GetData();
}
public class SQLDataProvider : IDataProvider
{
    private readonly IRepository _repository { get; set; }
    public SQLDataProvider(IRepository repository)
    {
        _repository = repository;
    }
    public List<string> GetData()
    {
        return new List<string> { "" };
    }
}
public class MockDataProvider : IDataProvider
{
    public List<string> GetData()
    {
        return new List<string> { "" };
    }
}
class Program
{
 static void Main(string[] args)
 {
    string targetClass = "SQLDataProvider";
    //Here i need to supply IRepository instance too 
   IDataProvider dataProvider = 
   (IDataProvider)Activator.CreateInstance(typeof(IDataProvider), targetClass);

  }
}

Какой лучший код я делаю и поставляю экземпляр другого объекта для параметра конструктора?

4b9b3361

Ответ 1

Вы можете написать контейнер всего несколькими строками кода. По своей сути это, как правило, словарь, который с System.Type является его ключом, а значением будет некоторый объект, который позволяет создавать новые экземпляры этого типа. Когда вы напишете простую реализацию, System.Func<object> будет делать. Вот простая реализация, которая содержит несколько методов Register, как общий, так и не общий метод GetInstance, и позволяет авто-проводку:

public class Container 
{
    Dictionary<Type, Func<object>> registrations = new Dictionary<Type, Func<object>>();

    public void Register<TService, TImpl>() where TImpl : TService {
        this.registrations.Add(typeof(TService), () => this.GetInstance(typeof(TImpl)));
    }

    public void Register<TService>(Func<TService> instanceCreator) {
        this.registrations.Add(typeof(TService), () => instanceCreator());
    }

    public void RegisterSingleton<TService>(TService instance) {
        this.registrations.Add(typeof(TService), () => instance);
    }

    public void RegisterSingleton<TService>(Func<TService> instanceCreator) {
        var lazy = new Lazy<TService>(instanceCreator);
        this.Register<TService>(() => lazy.Value);
    }

    public object GetInstance(Type serviceType) {
        Func<object> creator;
        if (this.registrations.TryGetValue(serviceType, out creator)) return creator();
        else if (!serviceType.IsAbstract) return this.CreateInstance(serviceType);
        else throw new InvalidOperationException("No registration for " + serviceType);
    }

    private object CreateInstance(Type implementationType) {
        var ctor = implementationType.GetConstructors().Single();
        var parameterTypes = ctor.GetParameters().Select(p => p.ParameterType);
        var dependencies = parameterTypes.Select(t => this.GetInstance(t)).ToArray();
        return Activator.CreateInstance(implementationType, dependencies);
    }
}

Вы можете использовать его следующим образом:

var container = new Container();

container.RegisterSingleton<ILogger>(new FileLogger("c:\\logs\\log.txt"));

// SqlUserRepository depends on ILogger
container.Register<IUserRepository, SqlUserRepository>();

// HomeController depends on IUserRepository
// Concrete instances don't need to be resolved
container.GetInstance(typeof(HomeController));

Внимание

Обратите внимание, что вы никогда не должны использовать такую ​​реализацию. В нем не хватает многих важных функций, которые вам предоставляют библиотеки DI, но не дает преимуществ перед использованием Pure DI (т.е. Графиков объектов ручной проводки). Вы теряете поддержку времени компиляции, не получая ничего обратно.

Когда ваше приложение малое, вы должны начать с Pure DI и после того, как ваше приложение и ваша конфигурация DI будут расти до такой степени, что вы будете поддерживать Root of Composition становится громоздким, вы можете рассмотреть возможность переключения на одну из установленных библиотек DI.

Вот некоторые из функций, которых эта наивная реализация не хватает по сравнению с установленными библиотеками:

  • Регистрация партии (регистрация набора типов с одной строкой)
  • Применение декораторов или перехватчиков для ряда типов
  • Отображение открытых общих абстракций для открытия общих реализаций
  • Интеграция с общими платформами приложений (такими как ASP.NET MVC, Web API и т.д.)
  • Регистрация типов с пользовательским образом жизни.
  • Достойная отчетность об ошибках (вместо исключения исключений, например)
  • Инструменты для проверки правильности конфигурации (чтобы компенсировать потерю поддержки времени компиляции) и диагностики распространенных ошибок конфигурации.
  • Хорошая производительность.

Эти функции позволяют поддерживать конфигурацию DI.

Ответ 2

Это уже несколько лет, но Айенде однажды написал сообщение в блоге об этом:
Создание контейнера IoC в 15 строках кода

Но это только самая простая возможная реализация.
Сам Айенде заявил в своем следующем сообщении, что существующие контейнеры IoC могут делать гораздо больше, чем просто возвращать экземпляры классов, - и здесь он становится сложным.
Как "Поверь мне, я доктор" уже сказал в своем комментарии: реализация полного контейнера IoC - это все, но тривиальное.