简单的依赖解析器 [英] Simple Dependency Resolver

查看:111
本文介绍了简单的依赖解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何创建一个简单的依赖解析器,与出使用任何内置或图书馆等Autofac,Ninject等。



这是我的面试问题。



我写了这个简单的代码,他们说这不好看。它像非常硬编码的想法。

 公共接口IRepository {} 
接口IDataProvider
{
文件清单<串GT;的GetData();
}
公共类的SqlDataProvider:IDataProvider
{
私人只读IRepository _repository {搞定;组; }
公众的SqlDataProvider(IRepository库)
{
_repository =库;
}
公开名单<串GT;的GetData()
{
返回新的List<串GT; {};
}
}
公共类MockDataProvider:IDataProvider
{
公开名单<串GT;的GetData()
{
返回新的List<串GT; {};
}
}
类节目
{
静态无效的主要(字串[] args)
{
串targetClass =的SqlDataProvider;
//这里我需要太$ B $提供IRepository实例b IDataProvider的dataProvider =
(IDataProvider)Activator.CreateInstance(typeof运算(IDataProvider),targetClass);

}
}



我该怎么办更好的代码和供应对于构造函数的参数其他对象的实例?


解决方案

您可以写在短短的几行代码的容器。其核心是通常是一个字典,用的System.Type 作为它的键和值是一些对象,它允许您创建该类型的新实例。当你写一个简单的实施 System.Func<对象> 会怎么做。这是一个包含多个注册方法简单的实现中,通用和非通用的GetInstance 方法,并允许自动接线方式:



<预类=郎-CS prettyprint-覆盖> 公共类集装箱
{
字典<类型,函数功能:LT ;对象>>注册=新词典<类型,函数功能与LT;对象>>();

公共无效注册< TService,TImpl>()其中TImpl:TService {
this.registrations.Add(typeof运算(TService),()=> this.GetInstance(typeof运算(TImpl )));
}

公共无效注册< TService>(Func键< TService> instanceCreator){
this.registrations.Add(typeof运算(TService),()=> instanceCreator() );
}

公共无效RegisterSingleton< TService>(TService实例){
this.registrations.Add(typeof运算(TService),()=>实例);
}

公共无效RegisterSingleton< TService>(Func键< TService> instanceCreator){
变种为lazy =新懒人< TService>(instanceCreator);
this.Register< TService>(()=> lazy.Value);
}

公共对象的GetInstance(类型的serviceType){
Func键<对象>创造者;
如果(this.registrations.TryGetValue(的serviceType,出创作者))返回创建者(); (!serviceType.IsAbstract)
,否则如果返回this.CreateInstance(的serviceType);
,否则抛出新的InvalidOperationException异常(没有登记+的serviceType);
}

私有对象的CreateInstance(类型implementationType){
VAR男星= implementationType.GetConstructors()单()。
VAR parameterTypes = ctor.GetParameters()选择(P => p.ParameterType)。
变种依赖= parameterTypes.Select(T => this.GetInstance(t))的ToArray的()。
返回Activator.CreateInstance(implementationType,依赖);
}
}

您可以按如下方式使用它:



<预类=郎-CS prettyprint-覆盖> VAR集装箱=​​新容器();

container.RegisterSingleton< ILogger>(新FileLogger(C:\\logs\\log.txt));

// SqlUserRepository取决于ILogger
container.Register< IUserRepository,SqlUserRepository>();

//的HomeController取决于IUserRepository
//混凝土实例不需要解决
container.GetInstance(typeof运算(HomeController中));

请注意,海事组织你应该从来没有真正使用这种执行。它缺乏DI库给你许多重要功能,但给出了使用纯没有任何优势DI (即手工布线对象图)。你失去编译时的支持,没有得到任何回报。



当你的应用是小,你应该用纯DI,一旦你的应用程序和DI配置成长的开始点,保持您成分根变得麻烦,你可以考虑改用之一。建立DI库



下面是一些这种幼稚的做法没啥相比,建立图书馆的功能:




  • 批量注册(注册用一个线上的一组类型)

  • 将装饰或拦截器为一系列的类型

  • 映射开放式泛型抽象打开通用实施

  • 集成了常见的应用平台(如ASP.NET MVC,网络API等)

  • 注册类型定制的生活方式。

  • 体面的错误报告(而不是抛出的堆栈溢出异常举例)

  • 工具验证配置的正确性(以补偿的编译时支持的损失)和诊断常见的配置错误。

  • 不错的表现。



这些功能让你保持你的DI配置维护。


How do you create simple Dependency Resolver, with out using any built in or library such as Autofac, Ninject, etc.

This was my interview question.

I wrote this simple code and they said it does not look good. Its like very hard coded idea.

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);

  }
}

What better code i do and supply other object instance for constructor parameter?

解决方案

You can write a container in just a few lines of code. At its core it would typically be a dictionary that with System.Type as its key and the value would be some object that allows you to create new instances of that type. When you write a simple implementation System.Func<object> would do. Here is a simple implementation that contains several Register methods, both a generic and non-generic GetInstance method and allows auto-wiring:

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);
    }
}

You can use it as follows:

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));

Please note that IMO you should never actually use such implementation. It lacks many important features that DI libraries give you, yet gives no advantage over using Pure DI (i.e. hand-wiring object graphs). You loose compile-time support, without getting anything back.

When your application is small, you should start with Pure DI and once your application and your DI configuration grow to the point that maintaining you Composition Root becomes cumbersome, you could consider switching to one of the established DI libraries.

Here are some of the features that this naive implementation lacks compared to the established libraries:

  • Batch registration (registering a set of types with a single line)
  • Applying decorators or interceptors for a range of types
  • Mapping open generic abstractions to open generic implementations
  • Integration with common application platforms (such as ASP.NET MVC, Web API, etc)
  • Registering types with custom lifestyles.
  • Decent error reporting (instead of throwing stack overflow exceptions for instance)
  • Tools for verifying the correctness of the configuration (to compensate the loss of compile-time support) and diagnosing common configuration mistakes.
  • Good performance.

These features allow you to keep your DI configuration maintainable.

这篇关于简单的依赖解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆