Ответ 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.