2013-03-30 161 views
22

如何创建简单的依赖解析器,使用任何内置或图书馆等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); 

    } 
} 

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

+5

大多数IoC容器都是开源的,例如, [SimpleInjector](http://simpleinjector.codeplex.com/)和[Autofac](http://code.google.com/p/autofac/source/browse/),因此您可以窥探灵感。警告字 - 实现*不是微不足道的。 –

+9

在一个侧面说明,这是一个令人难以置信的弱智面试问题。 – Filip

+2

这个问题似乎是脱离主题,因为它基本上是要求代码审查。它属于“代码评审”。 –

回答

4

它已经几十岁,但Ayende曾经写过关于这个博客文章:
Building an IoC container in 15 lines of code

但这只是非常简单的可能的实现。
Ayende在他的next post中表示,现有的IoC容器可以做更多的事情而不仅仅是返回类实例 - 而这正是它变得复杂的地方。作为“相信我 - 我是医生”已经在他的评论中说过:实施完整 IoC容器是一切,但微不足道。

29

只需几行代码即可编写容器。在它的核心,它通常是一个以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(即手工连接的对象图)相比没有优势。你松散的编译时支持,没有得到任何回报。

当你的应用程序是小,你应该用纯DI一旦你的应用程序和DI配置成长到维护您Composition Root变得麻烦,你可以考虑切换到建立DI库的一个点开始。

下面是一些,这幼稚实施缺乏相比建立库的特征:

  • 批量注册(注册一组类型与单个线)
  • 应用装饰或拦截器为一系列的类型
  • 映射开放通用抽象打开通用实现
  • 集成公共应用平台(如ASP.NET MVC,网络API等)
  • 注册具有自定义生活方式的类型。
  • 正确的错误报告(而不是抛出堆栈溢出例外)
  • 验证配置的正确性(以补偿编译时支持的损失)和诊断常见配置错误的工具。
  • 良好的表现。

这些功能可让您保持DI配置的可维护性。

+1

酷一个很好 – Billa

+0

这里container.GetInstance(typeof(HomeController))返回对象类型,是否有可能TypeCast并从GetInstance()方法本身返回HomeController类型,我试过http://stackoverflow.com/questions/3675/创建自定义容器类在c - 尖锐?noredirect = 1#comment61083046_36750123在此代码中,但获取一些sytax错误 – Mathiyazhagan