简单的依赖性解析器

Bil*_*lla 27 c# design-patterns dependency-injection

如何创建简单的依赖关系解析器,不使用任何内置或库,如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);

  }
}
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*ven 32

您只需几行代码即可编写容器.从本质上讲,它通常是一个字典,其中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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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));
Run Code Online (Sandbox Code Playgroud)

警告:

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

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

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

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

这些功能使您可以保持DI配置的可维护性.


Chr*_*cht 6

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

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