gpt4 book ai didi

c# - 简单的依赖解析器

转载 作者:IT王子 更新时间:2023-10-29 04:16:46 24 4
gpt4 key购买 nike

如何在不使用任何内置或库(例如 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);

}
}

有什么更好的代码可以为构造函数参数提供其他对象实例?

最佳答案

DI Containers是复杂的图书馆。 build 它们需要数年时间,维护它们需要数十年时间。但是为了演示它们的工作原理,您只需几行代码就可以编写一个简单的实现。

DI 容器的核心通常会用 System.Type 包装一个字典。作为它的键,值将是一些允许您创建该类型的新实例的对象。当您编写一个简单的实现时 System.Func<object>会做。这是一个包含多个 Register 的示例方法,通用和非通用 GetInstance方法并允许 Auto-Wiring :

public class Container
{
private readonly Dictionary<Type, Func<object>> regs = new();

public void Register<TService, TImpl>() where TImpl : TService =>
regs.Add(typeof(TService), () => this.GetInstance(typeof(TImpl)));

public void Register<TService>(Func<TService> factory) =>
regs.Add(typeof(TService), () => factory());

public void RegisterInstance<TService>(TService instance) =>
regs.Add(typeof(TService), () => instance);

public void RegisterSingleton<TService>(Func<TService> factory)
{
var lazy = new Lazy<TService>(factory);
Register(() => lazy.Value);
}

public object GetInstance(Type type)
{
if (regs.TryGetValue(type, out Func<object> fac)) return fac();
else if (!type.IsAbstract) return this.CreateInstance(type);
throw new InvalidOperationException("No registration for " + type);
}

private object CreateInstance(Type implementationType)
{
var ctor = implementationType.GetConstructors().Single();
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType);
var dependencies = paramTypes.Select(GetInstance).ToArray();
return Activator.CreateInstance(implementationType, dependencies);
}
}

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

var container = new Container();

container.RegisterInstance<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));

WARNING: You should never use such naive and simplistic implementation as given above. It lacks many important features that mature DI libraries give you, yet gives no advantage over using Pure DI (i.e. hand wiring object graphs). You lose compile-time support, without getting anything back.

当您的应用程序很小时,您应该从纯 DI 开始,一旦您的应用程序和 DI 配置增长到需要维护的程度Composition Root变得麻烦,您可以考虑切换到已建立的 DI 库之一。

与已建立的库相比,这里是这种天真的实现所缺乏的一些特性:

  • 自动注册:通过在单行中注册一组类型来应用约定优于配置的能力,而不必手动注册每个类型。
  • 拦截:为一系列类型应用装饰器或拦截器的能力
  • Generics:将开放式通用抽象映射到开放式通用实现
  • 集成:将库与常见应用程序平台(如 ASP.NET MVC、Web API、.NET Core 等)结合使用
  • 生命周期管理:使用自定义生活方式(例如 Scoped 或 Per Request)注册类型的能力。
  • 错误处理:检测错误配置,例如循环依赖。这种简单的实现会引发堆栈溢出异常。
  • 验证:用于验证配置正确性(以补偿编译时支持的损失)和诊断常见配置错误的功能或工具。
  • 性能:使用这种简单的实现方式构建大型对象图会很慢(例如,由于产生的垃圾量会导致很大的 GC 压力)。

这些特性和能力允许您在使用 DI 容器时保持 DI 配置的可维护性。

关于c# - 简单的依赖解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15715978/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com