简单的依赖性解析器

如何创建简单的依赖关系解析器,不使用任何内置或库,如Autofac,Ninject等。

这是我的面试问题。

我写了这个简单的代码,他们说它看起来不太好。 它就像非常难以编码的想法。

public interface IRepository { } interface IDataProvider { List GetData(); } public class SQLDataProvider : IDataProvider { private readonly IRepository _repository { get; set; } public SQLDataProvider(IRepository repository) { _repository = repository; } public List GetData() { return new List { "" }; } } public class MockDataProvider : IDataProvider { public List GetData() { return new List { "" }; } } 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); } } 

我做了什么更好的代码,并为构造函数参数提供其他对象实例?

您只需几行代码即可编写容器。 从System.Type ,它通常是一个字典,以System.Type作为其键,值将是一些允许您创建该类型的新实例的对象。 当你编写一个简单的实现System.Func会这样做。 这是一个简单的实现,它包含几个Register方法,包括generics和非genericsGetInstance方法,并允许自动连接:

 public class Container { Dictionary> registrations = new Dictionary>(); public void Register() where TImpl : TService { this.registrations.Add(typeof(TService), () => this.GetInstance(typeof(TImpl))); } public void Register(Func instanceCreator) { this.registrations.Add(typeof(TService), () => instanceCreator()); } public void RegisterSingleton(TService instance) { this.registrations.Add(typeof(TService), () => instance); } public void RegisterSingleton(Func instanceCreator) { var lazy = new Lazy(instanceCreator); this.Register(() => lazy.Value); } public object GetInstance(Type serviceType) { Func 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(new FileLogger("c:\\logs\\log.txt")); // SqlUserRepository depends on ILogger container.Register(); // HomeController depends on IUserRepository // Concrete instances don't need to be resolved container.GetInstance(typeof(HomeController)); 

警告

请注意,您永远不应该实际使用此类实现。 它缺少DI库给你的许多重要function,但没有使用Pure DI (即手工布线对象图)。 你失去了编译时的支持,没有得到任何回报。

当您的应用程序很小时,您应该从Pure DI开始,一旦您的应用程序和DI配置增长到维持组合根变得麻烦,您可以考虑切换到已建立的DI库之一。

以下是与已建立的库相比,这种天真实现缺少的一些function:

  • 批量注册(使用单行注册一组类型)
  • 为一系列类型应用装饰器或拦截器
  • 将开放式通用抽象映射到开放通用实现
  • 与通用应用程序平台(如ASP.NET MVC,Web API等)集成
  • 使用自定义生活方式注册类型。
  • 体面的错误报告(而不是抛出堆栈溢出exception)
  • 用于validation配置正确性(以补偿编译时支持的丢失)和诊断常见配置错误的工具。
  • 很好的表现。

这些function使您可以保持DI配置的可维护性。

它已经有几年了,但Ayende曾写过一篇关于此的博文:
用15行代码构建一个IoC容器

但这只是最简单的可能实现。
Ayende在他的下一篇文章中表示,现有的IoC容器可以做更多的事情,而不仅仅是返回类实例 – 这就是它变得复杂的地方。
正如“相信我 – 我是医生”在他的评论中已经说过:实现一个完整的 IoC容器是一切都是微不足道的。